blob: 773cc9ef98ddbbfac477f81960feb4150f06eb87 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.drawable.cts;
import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.cts.R;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.util.AttributeSet;
import android.util.Xml;
import android.widget.ImageView;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AnimatedVectorDrawableTest {
private static final int IMAGE_WIDTH = 64;
private static final int IMAGE_HEIGHT = 64;
private static final long MAX_TIMEOUT_MS = 1000;
private static final int MS_TO_NS = 1000000;
@Rule
public ActivityTestRule<DrawableStubActivity> mActivityRule =
new ActivityTestRule<DrawableStubActivity>(DrawableStubActivity.class);
private Activity mActivity;
private Resources mResources;
private static final boolean DBG_DUMP_PNG = false;
private final int mResId = R.drawable.animation_vector_drawable_grouping_1;
private final int mLayoutId = R.layout.animated_vector_drawable_source;
private final int mImageViewId = R.id.avd_view;
@Before
public void setup() {
mActivity = mActivityRule.getActivity();
mResources = mActivity.getResources();
}
@SmallTest
@Test
public void testInflate() throws Exception {
// Setup AnimatedVectorDrawable from xml file
XmlPullParser parser = mResources.getXml(mResId);
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty loop
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
Bitmap bitmap = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
AnimatedVectorDrawable drawable = new AnimatedVectorDrawable();
drawable.inflate(mResources, parser, attrs);
drawable.setBounds(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
bitmap.eraseColor(0);
drawable.draw(canvas);
int sunColor = bitmap.getPixel(IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2);
int earthColor = bitmap.getPixel(IMAGE_WIDTH * 3 / 4 + 2, IMAGE_HEIGHT / 2);
assertTrue(sunColor == 0xFFFF8000);
assertTrue(earthColor == 0xFF5656EA);
if (DBG_DUMP_PNG) {
DrawableTestUtils.saveAutoNamedVectorDrawableIntoPNG(mActivity, bitmap, mResId, null);
}
}
@Test
public void testGetChangingConfigurations() {
AnimatedVectorDrawable avd = new AnimatedVectorDrawable();
ConstantState constantState = avd.getConstantState();
// default
assertEquals(0, constantState.getChangingConfigurations());
assertEquals(0, avd.getChangingConfigurations());
// change the drawable's configuration does not affect the state's configuration
avd.setChangingConfigurations(0xff);
assertEquals(0xff, avd.getChangingConfigurations());
assertEquals(0, constantState.getChangingConfigurations());
// the state's configuration get refreshed
constantState = avd.getConstantState();
assertEquals(0xff, constantState.getChangingConfigurations());
// set a new configuration to drawable
avd.setChangingConfigurations(0xff00);
assertEquals(0xff, constantState.getChangingConfigurations());
assertEquals(0xffff, avd.getChangingConfigurations());
}
@Test
public void testGetConstantState() {
AnimatedVectorDrawable AnimatedVectorDrawable = new AnimatedVectorDrawable();
ConstantState constantState = AnimatedVectorDrawable.getConstantState();
assertNotNull(constantState);
assertEquals(0, constantState.getChangingConfigurations());
AnimatedVectorDrawable.setChangingConfigurations(1);
constantState = AnimatedVectorDrawable.getConstantState();
assertNotNull(constantState);
assertEquals(1, constantState.getChangingConfigurations());
}
@SmallTest
@Test
public void testMutate() {
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
AnimatedVectorDrawable d2 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
AnimatedVectorDrawable d3 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
int restoreAlpha = d1.getAlpha();
try {
int originalAlpha = d2.getAlpha();
int newAlpha = (originalAlpha + 1) % 255;
// AVD is different than VectorDrawable. Every instance of it is a deep copy
// of the VectorDrawable.
// So every setAlpha operation will happen only to that specific object.
d1.setAlpha(newAlpha);
assertEquals(newAlpha, d1.getAlpha());
assertEquals(originalAlpha, d2.getAlpha());
assertEquals(originalAlpha, d3.getAlpha());
d1.mutate();
d1.setAlpha(0x40);
assertEquals(0x40, d1.getAlpha());
assertEquals(originalAlpha, d2.getAlpha());
assertEquals(originalAlpha, d3.getAlpha());
d2.setAlpha(0x20);
assertEquals(0x40, d1.getAlpha());
assertEquals(0x20, d2.getAlpha());
assertEquals(originalAlpha, d3.getAlpha());
} finally {
mResources.getDrawable(mResId).setAlpha(restoreAlpha);
}
}
@SmallTest
@Test
public void testGetOpacity() {
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
assertEquals("Default is translucent", PixelFormat.TRANSLUCENT, d1.getOpacity());
d1.setAlpha(0);
assertEquals("Still translucent", PixelFormat.TRANSLUCENT, d1.getOpacity());
}
@SmallTest
@Test
public void testColorFilter() {
PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
d1.setColorFilter(filter);
assertEquals(filter, d1.getColorFilter());
}
@Test
public void testReset() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
d1.registerAnimationCallback(callback);
d1.start();
d1.reset();
});
waitForAVDStop(callback, MAX_TIMEOUT_MS);
assertFalse(d1.isRunning());
}
@Test
public void testStop() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
d1.registerAnimationCallback(callback);
d1.start();
d1.stop();
});
waitForAVDStop(callback, MAX_TIMEOUT_MS);
assertFalse(d1.isRunning());
}
@Test
public void testAddCallbackBeforeStart() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.registerAnimationCallback(callback);
d1.start();
});
callback.waitForStart();
waitForAVDStop(callback, MAX_TIMEOUT_MS);
callback.assertStarted(true);
callback.assertEnded(true);
}
@Test
public void testAddCallbackAfterTrigger() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
// This reset call can enforce the AnimatorSet is setup properly in AVD, when
// running on UI thread.
d1.reset();
d1.registerAnimationCallback(callback);
d1.start();
});
callback.waitForStart();
waitForAVDStop(callback, MAX_TIMEOUT_MS);
callback.assertStarted(true);
callback.assertEnded(true);
}
@Test
public void testAddCallbackAfterStart() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.start();
d1.registerAnimationCallback(callback);
});
callback.waitForStart();
waitForAVDStop(callback, MAX_TIMEOUT_MS);
// Whether or not the callback.start is true could vary when running on Render Thread.
// Therefore, we don't make assertion here. The most useful flag is the callback.mEnded.
callback.assertEnded(true);
callback.assertAVDRuntime(0, 400 * MS_TO_NS); // 4 times of the duration of the AVD.
}
@Test
public void testRemoveCallback() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.registerAnimationCallback(callback);
assertTrue(d1.unregisterAnimationCallback(callback));
d1.start();
});
callback.waitForStart();
waitForAVDStop(callback, MAX_TIMEOUT_MS);
callback.assertStarted(false);
callback.assertEnded(false);
}
@Test
public void testClearCallback() throws Throwable {
final Animatable2Callback callback = new Animatable2Callback();
// The AVD has a duration as 100ms.
mActivityRule.runOnUiThread(() -> {
mActivity.setContentView(mLayoutId);
ImageView imageView = (ImageView) mActivity.findViewById(mImageViewId);
AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) imageView.getDrawable();
d1.registerAnimationCallback(callback);
d1.clearAnimationCallbacks();
d1.start();
});
callback.waitForStart();
waitForAVDStop(callback, MAX_TIMEOUT_MS);
callback.assertStarted(false);
callback.assertEnded(false);
}
// The time out is expected when the listener is removed successfully.
// Such that we don't get the end event.
static void waitForAVDStop(Animatable2Callback callback, long timeout) {
try {
callback.waitForEnd(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
fail("We should not see the AVD run this long time!");
}
}
}