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 android.graphics.drawable.cts; 18 19 import static junit.framework.Assert.fail; 20 import static junit.framework.TestCase.assertFalse; 21 import static junit.framework.TestCase.assertTrue; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertNotNull; 25 26 import android.app.Activity; 27 import android.content.res.Resources; 28 import android.graphics.Bitmap; 29 import android.graphics.Canvas; 30 import android.graphics.Color; 31 import android.graphics.PixelFormat; 32 import android.graphics.PorterDuff; 33 import android.graphics.PorterDuffColorFilter; 34 import android.graphics.cts.R; 35 import android.graphics.drawable.Animatable2; 36 import android.graphics.drawable.AnimatedVectorDrawable; 37 import android.graphics.drawable.Drawable; 38 import android.graphics.drawable.Drawable.ConstantState; 39 import android.support.test.filters.LargeTest; 40 import android.support.test.filters.SmallTest; 41 import android.support.test.rule.ActivityTestRule; 42 import android.support.test.runner.AndroidJUnit4; 43 import android.util.AttributeSet; 44 import android.util.Xml; 45 import android.widget.ImageView; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.xmlpull.v1.XmlPullParser; 52 import org.xmlpull.v1.XmlPullParserException; 53 54 @LargeTest 55 @RunWith(AndroidJUnit4.class) 56 public class AnimatedVectorDrawableTest { 57 private static final int IMAGE_WIDTH = 64; 58 private static final int IMAGE_HEIGHT = 64; 59 private static final long MAX_TIMEOUT_MS = 1000; 60 private static final int MS_TO_NS = 1000000; 61 62 @Rule 63 public ActivityTestRule<DrawableStubActivity> mActivityRule = 64 new ActivityTestRule<DrawableStubActivity>(DrawableStubActivity.class); 65 private Activity mActivity; 66 private Resources mResources; 67 private static final boolean DBG_DUMP_PNG = false; 68 private final int mResId = R.drawable.animation_vector_drawable_grouping_1; 69 private final int mLayoutId = R.layout.animated_vector_drawable_source; 70 private final int mImageViewId = R.id.avd_view; 71 72 @Before 73 public void setup() { 74 mActivity = mActivityRule.getActivity(); 75 mResources = mActivity.getResources(); 76 } 77 78 @SmallTest 79 @Test 80 public void testInflate() throws Exception { 81 // Setup AnimatedVectorDrawable from xml file 82 XmlPullParser parser = mResources.getXml(mResId); 83 AttributeSet attrs = Xml.asAttributeSet(parser); 84 85 int type; 86 while ((type=parser.next()) != XmlPullParser.START_TAG && 87 type != XmlPullParser.END_DOCUMENT) { 88 // Empty loop 89 } 90 91 if (type != XmlPullParser.START_TAG) { 92 throw new XmlPullParserException("No start tag found"); 93 } 94 Bitmap bitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888); 95 Canvas canvas = new Canvas(bitmap); 96 AnimatedVectorDrawable drawable = new AnimatedVectorDrawable(); 97 drawable.inflate(mResources, parser, attrs); 98 drawable.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); 99 bitmap.eraseColor(0); 100 drawable.draw(canvas); 101 int sunColor = bitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2); 102 int earthColor = bitmap.getPixel(IMAGE_WIDTH * 3 / 4 + 2, IMAGE_HEIGHT / 2); 103 assertTrue(sunColor == 0xFFFF8000); 104 assertTrue(earthColor == 0xFF5656EA); 105 106 if (DBG_DUMP_PNG) { 107 DrawableTestUtils.saveAutoNamedVectorDrawableIntoPNG(mActivity, bitmap, mResId, null); 108 } 109 } 110 111 @Test 112 public void testGetChangingConfigurations() { 113 AnimatedVectorDrawable avd = new AnimatedVectorDrawable(); 114 ConstantState constantState = avd.getConstantState(); 115 116 // default 117 assertEquals(0, constantState.getChangingConfigurations()); 118 assertEquals(0, avd.getChangingConfigurations()); 119 120 // change the drawable's configuration does not affect the state's configuration 121 avd.setChangingConfigurations(0xff); 122 assertEquals(0xff, avd.getChangingConfigurations()); 123 assertEquals(0, constantState.getChangingConfigurations()); 124 125 // the state's configuration get refreshed 126 constantState = avd.getConstantState(); 127 assertEquals(0xff, constantState.getChangingConfigurations()); 128 129 // set a new configuration to drawable 130 avd.setChangingConfigurations(0xff00); 131 assertEquals(0xff, constantState.getChangingConfigurations()); 132 assertEquals(0xffff, avd.getChangingConfigurations()); 133 } 134 135 @Test 136 public void testGetConstantState() { 137 AnimatedVectorDrawable AnimatedVectorDrawable = new AnimatedVectorDrawable(); 138 ConstantState constantState = AnimatedVectorDrawable.getConstantState(); 139 assertNotNull(constantState); 140 assertEquals(0, constantState.getChangingConfigurations()); 141 142 AnimatedVectorDrawable.setChangingConfigurations(1); 143 constantState = AnimatedVectorDrawable.getConstantState(); 144 assertNotNull(constantState); 145 assertEquals(1, constantState.getChangingConfigurations()); 146 } 147 148 @SmallTest 149 @Test 150 public void testMutate() { 151 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 152 AnimatedVectorDrawable d2 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 153 AnimatedVectorDrawable d3 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 154 int restoreAlpha = d1.getAlpha(); 155 156 try { 157 int originalAlpha = d2.getAlpha(); 158 int newAlpha = (originalAlpha + 1) % 255; 159 160 // AVD is different than VectorDrawable. Every instance of it is a deep copy 161 // of the VectorDrawable. 162 // So every setAlpha operation will happen only to that specific object. 163 d1.setAlpha(newAlpha); 164 assertEquals(newAlpha, d1.getAlpha()); 165 assertEquals(originalAlpha, d2.getAlpha()); 166 assertEquals(originalAlpha, d3.getAlpha()); 167 168 d1.mutate(); 169 d1.setAlpha(0x40); 170 assertEquals(0x40, d1.getAlpha()); 171 assertEquals(originalAlpha, d2.getAlpha()); 172 assertEquals(originalAlpha, d3.getAlpha()); 173 174 d2.setAlpha(0x20); 175 assertEquals(0x40, d1.getAlpha()); 176 assertEquals(0x20, d2.getAlpha()); 177 assertEquals(originalAlpha, d3.getAlpha()); 178 } finally { 179 mResources.getDrawable(mResId).setAlpha(restoreAlpha); 180 } 181 } 182 183 @SmallTest 184 @Test 185 public void testGetOpacity() { 186 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 187 assertEquals("Default is translucent", PixelFormat.TRANSLUCENT, d1.getOpacity()); 188 d1.setAlpha(0); 189 assertEquals("Still translucent", PixelFormat.TRANSLUCENT, d1.getOpacity()); 190 } 191 192 @SmallTest 193 @Test 194 public void testColorFilter() { 195 PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN); 196 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 197 d1.setColorFilter(filter); 198 199 assertEquals(filter, d1.getColorFilter()); 200 } 201 202 @Test 203 public void testReset() throws Throwable { 204 final Animatable2Callback callback = new Animatable2Callback(); 205 final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 206 // The AVD has a duration as 100ms. 207 mActivityRule.runOnUiThread(() -> { 208 d1.registerAnimationCallback(callback); 209 d1.start(); 210 d1.reset(); 211 }); 212 waitForAVDStop(callback, MAX_TIMEOUT_MS); 213 assertFalse(d1.isRunning()); 214 215 } 216 217 @Test 218 public void testStop() throws Throwable { 219 final Animatable2Callback callback = new Animatable2Callback(); 220 final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId); 221 // The AVD has a duration as 100ms. 222 mActivityRule.runOnUiThread(() -> { 223 d1.registerAnimationCallback(callback); 224 d1.start(); 225 d1.stop(); 226 }); 227 waitForAVDStop(callback, MAX_TIMEOUT_MS); 228 assertFalse(d1.isRunning()); 229 } 230 231 @Test 232 public void testAddCallbackBeforeStart() throws Throwable { 233 final Animatable2Callback callback = new Animatable2Callback(); 234 // The AVD has a duration as 100ms. 235 mActivityRule.runOnUiThread(() -> { 236 mActivity.setContentView(mLayoutId); 237 ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId); 238 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable(); 239 d1.registerAnimationCallback(callback); 240 d1.start(); 241 }); 242 callback.waitForStart(); 243 waitForAVDStop(callback, MAX_TIMEOUT_MS); 244 callback.assertStarted(true); 245 callback.assertEnded(true); 246 } 247 248 @Test 249 public void testAddCallbackAfterTrigger() throws Throwable { 250 final Animatable2Callback callback = new Animatable2Callback(); 251 // The AVD has a duration as 100ms. 252 mActivityRule.runOnUiThread(() -> { 253 mActivity.setContentView(mLayoutId); 254 ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId); 255 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable(); 256 // This reset call can enforce the AnimatorSet is setup properly in AVD, when 257 // running on UI thread. 258 d1.reset(); 259 d1.registerAnimationCallback(callback); 260 d1.start(); 261 }); 262 callback.waitForStart(); 263 waitForAVDStop(callback, MAX_TIMEOUT_MS); 264 265 callback.assertStarted(true); 266 callback.assertEnded(true); 267 } 268 269 @Test 270 public void testAddCallbackAfterStart() throws Throwable { 271 final Animatable2Callback callback = new Animatable2Callback(); 272 // The AVD has a duration as 100ms. 273 mActivityRule.runOnUiThread(() -> { 274 mActivity.setContentView(mLayoutId); 275 ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId); 276 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable(); 277 d1.start(); 278 d1.registerAnimationCallback(callback); 279 }); 280 callback.waitForStart(); 281 282 waitForAVDStop(callback, MAX_TIMEOUT_MS); 283 // Whether or not the callback.start is true could vary when running on Render Thread. 284 // Therefore, we don't make assertion here. The most useful flag is the callback.mEnded. 285 callback.assertEnded(true); 286 callback.assertAVDRuntime(0, 400 * MS_TO_NS); // 4 times of the duration of the AVD. 287 } 288 289 @Test 290 public void testRemoveCallback() throws Throwable { 291 final Animatable2Callback callback = new Animatable2Callback(); 292 // The AVD has a duration as 100ms. 293 mActivityRule.runOnUiThread(() -> { 294 mActivity.setContentView(mLayoutId); 295 ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId); 296 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable(); 297 d1.registerAnimationCallback(callback); 298 assertTrue(d1.unregisterAnimationCallback(callback)); 299 d1.start(); 300 }); 301 callback.waitForStart(); 302 303 waitForAVDStop(callback, MAX_TIMEOUT_MS); 304 callback.assertStarted(false); 305 callback.assertEnded(false); 306 } 307 308 @Test 309 public void testClearCallback() throws Throwable { 310 final Animatable2Callback callback = new Animatable2Callback(); 311 312 // The AVD has a duration as 100ms. 313 mActivityRule.runOnUiThread(() -> { 314 mActivity.setContentView(mLayoutId); 315 ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId); 316 AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable(); 317 d1.registerAnimationCallback(callback); 318 d1.clearAnimationCallbacks(); 319 d1.start(); 320 }); 321 callback.waitForStart(); 322 323 waitForAVDStop(callback, MAX_TIMEOUT_MS); 324 callback.assertStarted(false); 325 callback.assertEnded(false); 326 } 327 328 // The time out is expected when the listener is removed successfully. 329 // Such that we don't get the end event. 330 static void waitForAVDStop(Animatable2Callback callback, long timeout) { 331 try { 332 callback.waitForEnd(timeout); 333 } catch (InterruptedException e) { 334 e.printStackTrace(); 335 fail("We should not see the AVD run this long time!"); 336 } 337 } 338 } 339