1 /* 2 * Copyright (C) 2016 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.transition.cts; 17 18 import static org.junit.Assert.assertEquals; 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertNull; 21 import static org.mockito.Matchers.any; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.times; 24 import static org.mockito.Mockito.verify; 25 26 import android.animation.Animator; 27 import android.animation.AnimatorListenerAdapter; 28 import android.graphics.Matrix; 29 import android.graphics.drawable.Drawable; 30 import android.support.test.filters.MediumTest; 31 import android.support.test.runner.AndroidJUnit4; 32 import android.transition.ChangeImageTransform; 33 import android.transition.TransitionManager; 34 import android.transition.TransitionValues; 35 import android.util.DisplayMetrics; 36 import android.util.TypedValue; 37 import android.view.ViewGroup; 38 import android.view.ViewGroup.LayoutParams; 39 import android.widget.ImageView; 40 import android.widget.ImageView.ScaleType; 41 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 @MediumTest 47 @RunWith(AndroidJUnit4.class) 48 public class ChangeImageTransformTest extends BaseTransitionTest { 49 ChangeImageTransform mChangeImageTransform; 50 Matrix mStartMatrix; 51 Matrix mEndMatrix; 52 Drawable mImage; 53 ImageView mImageView; 54 55 @Override 56 @Before 57 public void setup() { 58 super.setup(); 59 resetTransition(); 60 mStartMatrix = null; 61 mEndMatrix = null; 62 mImage = null; 63 mImageView = null; 64 } 65 66 private void resetTransition() { 67 mChangeImageTransform = new CaptureMatrix(); 68 mChangeImageTransform.setDuration(100); 69 mTransition = mChangeImageTransform; 70 resetListener(); 71 } 72 73 @Test 74 public void testCenterToFitXY() throws Throwable { 75 transformImage(ScaleType.CENTER, ScaleType.FIT_XY); 76 verifyMatrixMatches(centerMatrix(), mStartMatrix); 77 verifyMatrixMatches(fitXYMatrix(), mEndMatrix); 78 } 79 80 @Test 81 public void testCenterCropToFitCenter() throws Throwable { 82 transformImage(ScaleType.CENTER_CROP, ScaleType.FIT_CENTER); 83 verifyMatrixMatches(centerCropMatrix(), mStartMatrix); 84 verifyMatrixMatches(fitCenterMatrix(), mEndMatrix); 85 } 86 87 @Test 88 public void testCenterInsideToFitEnd() throws Throwable { 89 transformImage(ScaleType.CENTER_INSIDE, ScaleType.FIT_END); 90 // CENTER_INSIDE and CENTER are the same when the image is smaller than the View 91 verifyMatrixMatches(centerMatrix(), mStartMatrix); 92 verifyMatrixMatches(fitEndMatrix(), mEndMatrix); 93 } 94 95 @Test 96 public void testFitStartToCenter() throws Throwable { 97 transformImage(ScaleType.FIT_START, ScaleType.CENTER); 98 verifyMatrixMatches(fitStartMatrix(), mStartMatrix); 99 verifyMatrixMatches(centerMatrix(), mEndMatrix); 100 } 101 102 private Matrix centerMatrix() { 103 int imageWidth = mImage.getIntrinsicWidth(); 104 int imageViewWidth = mImageView.getWidth(); 105 float tx = Math.round((imageViewWidth - imageWidth)/2f); 106 107 int imageHeight = mImage.getIntrinsicHeight(); 108 int imageViewHeight = mImageView.getHeight(); 109 float ty = Math.round((imageViewHeight - imageHeight)/2f); 110 111 Matrix matrix = new Matrix(); 112 matrix.postTranslate(tx, ty); 113 return matrix; 114 } 115 116 private Matrix fitXYMatrix() { 117 int imageWidth = mImage.getIntrinsicWidth(); 118 int imageViewWidth = mImageView.getWidth(); 119 float scaleX = ((float)imageViewWidth)/imageWidth; 120 121 int imageHeight = mImage.getIntrinsicHeight(); 122 int imageViewHeight = mImageView.getHeight(); 123 float scaleY = ((float)imageViewHeight)/imageHeight; 124 125 Matrix matrix = new Matrix(); 126 matrix.postScale(scaleX, scaleY); 127 return matrix; 128 } 129 130 private Matrix centerCropMatrix() { 131 int imageWidth = mImage.getIntrinsicWidth(); 132 int imageViewWidth = mImageView.getWidth(); 133 float scaleX = ((float)imageViewWidth)/imageWidth; 134 135 int imageHeight = mImage.getIntrinsicHeight(); 136 int imageViewHeight = mImageView.getHeight(); 137 float scaleY = ((float)imageViewHeight)/imageHeight; 138 139 float maxScale = Math.max(scaleX, scaleY); 140 141 float width = imageWidth * maxScale; 142 float height = imageHeight * maxScale; 143 float tx = Math.round((imageViewWidth - width) / 2f); 144 float ty = Math.round((imageViewHeight - height) / 2f); 145 146 Matrix matrix = new Matrix(); 147 matrix.postScale(maxScale, maxScale); 148 matrix.postTranslate(tx, ty); 149 return matrix; 150 } 151 152 private Matrix fitCenterMatrix() { 153 int imageWidth = mImage.getIntrinsicWidth(); 154 int imageViewWidth = mImageView.getWidth(); 155 float scaleX = ((float)imageViewWidth)/imageWidth; 156 157 int imageHeight = mImage.getIntrinsicHeight(); 158 int imageViewHeight = mImageView.getHeight(); 159 float scaleY = ((float)imageViewHeight)/imageHeight; 160 161 float minScale = Math.min(scaleX, scaleY); 162 163 float width = imageWidth * minScale; 164 float height = imageHeight * minScale; 165 float tx = (imageViewWidth - width) / 2f; 166 float ty = (imageViewHeight - height) / 2f; 167 168 Matrix matrix = new Matrix(); 169 matrix.postScale(minScale, minScale); 170 matrix.postTranslate(tx, ty); 171 return matrix; 172 } 173 174 private Matrix fitStartMatrix() { 175 int imageWidth = mImage.getIntrinsicWidth(); 176 int imageViewWidth = mImageView.getWidth(); 177 float scaleX = ((float)imageViewWidth)/imageWidth; 178 179 int imageHeight = mImage.getIntrinsicHeight(); 180 int imageViewHeight = mImageView.getHeight(); 181 float scaleY = ((float)imageViewHeight)/imageHeight; 182 183 float minScale = Math.min(scaleX, scaleY); 184 185 Matrix matrix = new Matrix(); 186 matrix.postScale(minScale, minScale); 187 return matrix; 188 } 189 190 private Matrix fitEndMatrix() { 191 int imageWidth = mImage.getIntrinsicWidth(); 192 int imageViewWidth = mImageView.getWidth(); 193 float scaleX = ((float)imageViewWidth)/imageWidth; 194 195 int imageHeight = mImage.getIntrinsicHeight(); 196 int imageViewHeight = mImageView.getHeight(); 197 float scaleY = ((float)imageViewHeight)/imageHeight; 198 199 float minScale = Math.min(scaleX, scaleY); 200 201 float width = imageWidth * minScale; 202 float height = imageHeight * minScale; 203 float tx = imageViewWidth - width; 204 float ty = imageViewHeight - height; 205 206 Matrix matrix = new Matrix(); 207 matrix.postScale(minScale, minScale); 208 matrix.postTranslate(tx, ty); 209 return matrix; 210 } 211 212 private void verifyMatrixMatches(Matrix expected, Matrix matrix) { 213 if (expected == null) { 214 assertNull(matrix); 215 return; 216 } 217 assertNotNull(matrix); 218 float[] expectedValues = new float[9]; 219 expected.getValues(expectedValues); 220 221 float[] values = new float[9]; 222 matrix.getValues(values); 223 224 for (int i = 0; i < values.length; i++) { 225 final float expectedValue = expectedValues[i]; 226 final float value = values[i]; 227 assertEquals("Value [" + i + "]", expectedValue, value, 0.01f); 228 } 229 } 230 231 private void transformImage(ScaleType startScale, final ScaleType endScale) throws Throwable { 232 final ImageView imageView = enterImageViewScene(startScale); 233 mActivityRule.runOnUiThread(() -> { 234 TransitionManager.beginDelayedTransition(mSceneRoot, mChangeImageTransform); 235 imageView.setScaleType(endScale); 236 }); 237 waitForStart(); 238 verify(mListener, (startScale == endScale) ? times(1) : never()).onTransitionEnd(any()); 239 waitForEnd(1000); 240 } 241 242 private ImageView enterImageViewScene(final ScaleType scaleType) throws Throwable { 243 enterScene(R.layout.scene4); 244 final ViewGroup container = (ViewGroup) mActivity.findViewById(R.id.holder); 245 final ImageView[] imageViews = new ImageView[1]; 246 mActivityRule.runOnUiThread(() -> { 247 mImageView = new ImageView(mActivity); 248 mImage = mActivity.getDrawable(android.R.drawable.ic_media_play); 249 mImageView.setImageDrawable(mImage); 250 mImageView.setScaleType(scaleType); 251 imageViews[0] = mImageView; 252 container.addView(mImageView); 253 LayoutParams layoutParams = mImageView.getLayoutParams(); 254 DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics(); 255 float size = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, metrics); 256 layoutParams.width = Math.round(size); 257 layoutParams.height = Math.round(size * 2); 258 mImageView.setLayoutParams(layoutParams); 259 }); 260 mInstrumentation.waitForIdleSync(); 261 return imageViews[0]; 262 } 263 264 private class CaptureMatrix extends ChangeImageTransform { 265 @Override 266 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 267 TransitionValues endValues) { 268 Animator animator = super.createAnimator(sceneRoot, startValues, endValues); 269 animator.addListener(new CaptureMatrixListener((ImageView) endValues.view)); 270 return animator; 271 } 272 } 273 274 private class CaptureMatrixListener extends AnimatorListenerAdapter { 275 private final ImageView mImageView; 276 277 public CaptureMatrixListener(ImageView view) { 278 mImageView = view; 279 } 280 281 @Override 282 public void onAnimationStart(Animator animation) { 283 mStartMatrix = copyMatrix(); 284 } 285 286 @Override 287 public void onAnimationEnd(Animator animation) { 288 mEndMatrix = copyMatrix(); 289 } 290 291 private Matrix copyMatrix() { 292 Matrix matrix = mImageView.getImageMatrix(); 293 if (matrix != null) { 294 matrix = new Matrix(matrix); 295 } 296 return matrix; 297 } 298 } 299 } 300 301