AnimatedVectorDrawableTest.java revision fad2335f169d36b7b6f2c0ec8ddfe6c0094c2072
1/* 2 * Copyright (C) 2015 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 17package android.support.graphics.drawable.tests; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.graphics.Bitmap; 22import android.graphics.Canvas; 23import android.graphics.drawable.Drawable.ConstantState; 24import android.support.annotation.DrawableRes; 25import android.support.graphics.drawable.AnimatedVectorDrawableCompat; 26import android.support.graphics.drawable.animated.test.R; 27import android.support.test.InstrumentationRegistry; 28import android.support.test.rule.ActivityTestRule; 29import android.support.test.runner.AndroidJUnit4; 30import android.test.suitebuilder.annotation.MediumTest; 31import android.util.AttributeSet; 32import android.util.Log; 33import android.util.Xml; 34import android.view.View; 35import android.widget.ImageButton; 36import org.junit.Before; 37import org.junit.Rule; 38import org.junit.Test; 39import org.junit.runner.RunWith; 40import org.xmlpull.v1.XmlPullParser; 41import org.xmlpull.v1.XmlPullParserException; 42 43import java.io.File; 44import java.io.FileOutputStream; 45import java.io.IOException; 46import java.util.concurrent.CountDownLatch; 47import java.util.concurrent.TimeUnit; 48 49import static org.junit.Assert.*; 50 51@MediumTest 52@RunWith(AndroidJUnit4.class) 53public class AnimatedVectorDrawableTest { 54 @Rule public final ActivityTestRule<DrawableStubActivity> mActivityTestRule; 55 private static final String LOGTAG = AnimatedVectorDrawableTest.class.getSimpleName(); 56 57 private static final int IMAGE_WIDTH = 64; 58 private static final int IMAGE_HEIGHT = 64; 59 private static final @DrawableRes int DRAWABLE_RES_ID = 60 R.drawable.animation_vector_drawable_grouping_1; 61 62 private Context mContext; 63 private Resources mResources; 64 private AnimatedVectorDrawableCompat mAnimatedVectorDrawable; 65 private Bitmap mBitmap; 66 private Canvas mCanvas; 67 private static final boolean DBG_DUMP_PNG = false; 68 69 public AnimatedVectorDrawableTest() { 70 mActivityTestRule = new ActivityTestRule<>(DrawableStubActivity.class); 71 } 72 73 @Before 74 public void setup() throws Exception { 75 mBitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888); 76 mCanvas = new Canvas(mBitmap); 77 mContext = mActivityTestRule.getActivity(); 78 mResources = mContext.getResources(); 79 80 mAnimatedVectorDrawable = AnimatedVectorDrawableCompat.create(mContext, DRAWABLE_RES_ID); 81 } 82 83 // This is only for debugging or golden image (re)generation purpose. 84 private void saveVectorDrawableIntoPNG(Bitmap bitmap, int resId) throws IOException { 85 // Save the image to the disk. 86 FileOutputStream out = null; 87 try { 88 String outputFolder = "/sdcard/temp/"; 89 File folder = new File(outputFolder); 90 if (!folder.exists()) { 91 folder.mkdir(); 92 } 93 String originalFilePath = mResources.getString(resId); 94 File originalFile = new File(originalFilePath); 95 String fileFullName = originalFile.getName(); 96 String fileTitle = fileFullName.substring(0, fileFullName.lastIndexOf(".")); 97 String outputFilename = outputFolder + fileTitle + "_golden.png"; 98 File outputFile = new File(outputFilename); 99 if (!outputFile.exists()) { 100 outputFile.createNewFile(); 101 } 102 103 out = new FileOutputStream(outputFile, false); 104 bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); 105 Log.v(LOGTAG, "Write test No." + outputFilename + " to file successfully."); 106 } catch (Exception e) { 107 e.printStackTrace(); 108 } finally { 109 if (out != null) { 110 out.close(); 111 } 112 } 113 } 114 115 @Test 116 public void testInflate() throws Exception { 117 // Setup AnimatedVectorDrawableCompat from xml file 118 XmlPullParser parser = mResources.getXml(DRAWABLE_RES_ID); 119 AttributeSet attrs = Xml.asAttributeSet(parser); 120 121 int type; 122 while ((type = parser.next()) != XmlPullParser.START_TAG && 123 type != XmlPullParser.END_DOCUMENT) { 124 // Empty loop 125 } 126 127 if (type != XmlPullParser.START_TAG) { 128 throw new XmlPullParserException("No start tag found"); 129 } 130 131 mAnimatedVectorDrawable.inflate(mResources, parser, attrs); 132 mAnimatedVectorDrawable.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); 133 mBitmap.eraseColor(0); 134 mAnimatedVectorDrawable.draw(mCanvas); 135 int sunColor = mBitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2); 136 int earthColor = mBitmap.getPixel(IMAGE_WIDTH * 3 / 4 + 2, IMAGE_HEIGHT / 2); 137 assertTrue(sunColor == 0xFFFF8000); 138 assertTrue(earthColor == 0xFF5656EA); 139 140 if (DBG_DUMP_PNG) { 141 saveVectorDrawableIntoPNG(mBitmap, DRAWABLE_RES_ID); 142 } 143 } 144 145 @Test 146 public void testGetChangingConfigurations() { 147 AnimatedVectorDrawableCompat d1 = AnimatedVectorDrawableCompat.create(mContext, 148 R.drawable.animated_color_fill_copy); 149 ConstantState constantState = d1.getConstantState(); 150 151 if (constantState != null) { 152 // default 153 assertEquals(0, constantState.getChangingConfigurations()); 154 assertEquals(0, d1.getChangingConfigurations()); 155 156 // change the drawable's configuration does not affect the state's configuration 157 d1.setChangingConfigurations(0xff); 158 assertEquals(0xff, d1.getChangingConfigurations()); 159 assertEquals(0, constantState.getChangingConfigurations()); 160 161 // the state's configuration get refreshed 162 constantState = d1.getConstantState(); 163 assertEquals(0xff, constantState.getChangingConfigurations()); 164 165 // set a new configuration to drawable 166 d1.setChangingConfigurations(0xff00); 167 assertEquals(0xff, constantState.getChangingConfigurations()); 168 assertEquals(0xffff, d1.getChangingConfigurations()); 169 } 170 } 171 172 @Test 173 public void testGetConstantState() { 174 ConstantState constantState = mAnimatedVectorDrawable.getConstantState(); 175 if (constantState != null) { 176 assertEquals(0, constantState.getChangingConfigurations()); 177 178 mAnimatedVectorDrawable.setChangingConfigurations(1); 179 constantState = mAnimatedVectorDrawable.getConstantState(); 180 assertNotNull(constantState); 181 assertEquals(1, constantState.getChangingConfigurations()); 182 } 183 } 184 185 @Test 186 public void testAnimateColor() throws Throwable { 187 final ImageButton imageButton = 188 (ImageButton) mActivityTestRule.getActivity().findViewById(R.id.imageButton); 189 final int viewW = imageButton.getWidth(); 190 final int viewH = imageButton.getHeight(); 191 int pixelX = viewW / 2; 192 int pixelY = viewH / 2; 193 final int numTests = 5; 194 final Bitmap bitmap = Bitmap.createBitmap(imageButton.getWidth(), imageButton.getHeight(), 195 Bitmap.Config.ARGB_8888); 196 final Canvas c = new Canvas(bitmap); 197 CountDownLatch latch = new CountDownLatch(numTests); 198 199 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 200 @Override 201 public void run() { 202 AnimatedVectorDrawableCompat avd = AnimatedVectorDrawableCompat.create(mContext, 203 R.drawable.animated_color_fill); 204 imageButton.setBackgroundDrawable(avd); 205 avd.start(); 206 } 207 }); 208 // Check the view several times during the animation to verify that it only 209 // has red color in it 210 for (int i = 0; i < numTests; ++i) { 211 Thread.sleep(100); 212 // check fill 213 verifyRedOnly(pixelX, pixelY, imageButton, bitmap, c, latch); 214 // check stroke 215 verifyRedOnly(1, 1, imageButton, bitmap, c, latch); 216 } 217 latch.await(1000, TimeUnit.MILLISECONDS); 218 } 219 220 /** 221 * Utility method to verify that the pixel at the given location has only red values. 222 */ 223 private void verifyRedOnly(final int pixelX, final int pixelY, final View button, 224 final Bitmap bitmap, final Canvas canvas, final CountDownLatch latch) throws Throwable { 225 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 226 @Override 227 public void run() { 228 button.draw(canvas); 229 int pixel = bitmap.getPixel(pixelX, pixelY); 230 int blue = pixel & 0xff; 231 int green = pixel & 0xff00 >> 8; 232 assertEquals("Blue channel not zero", 0, blue); 233 assertEquals("Green channel not zero", 0, green); 234 latch.countDown(); 235 } 236 }); 237 } 238 239 @Test 240 public void testMutate() { 241 AnimatedVectorDrawableCompat d1 = 242 AnimatedVectorDrawableCompat.create(mContext, DRAWABLE_RES_ID); 243 AnimatedVectorDrawableCompat d2 = 244 AnimatedVectorDrawableCompat.create(mContext, DRAWABLE_RES_ID); 245 AnimatedVectorDrawableCompat d3 = 246 AnimatedVectorDrawableCompat.create(mContext, DRAWABLE_RES_ID); 247 248 if (d1.getConstantState() != null) { 249 int originalAlpha = d2.getAlpha(); 250 int newAlpha = (originalAlpha + 1) % 255; 251 252 // AVD is different than VectorDrawable. Every instance of it is a deep copy 253 // of the VectorDrawable. 254 // So every setAlpha operation will happen only to that specific object. 255 d1.setAlpha(newAlpha); 256 assertEquals(newAlpha, d1.getAlpha()); 257 assertEquals(originalAlpha, d2.getAlpha()); 258 assertEquals(originalAlpha, d3.getAlpha()); 259 260 d1.mutate(); 261 d1.setAlpha(0x40); 262 assertEquals(0x40, d1.getAlpha()); 263 assertEquals(originalAlpha, d2.getAlpha()); 264 assertEquals(originalAlpha, d3.getAlpha()); 265 266 d2.setAlpha(0x20); 267 assertEquals(0x40, d1.getAlpha()); 268 assertEquals(0x20, d2.getAlpha()); 269 assertEquals(originalAlpha, d3.getAlpha()); 270 } 271 } 272} 273