Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -2445,6 +2445,7 @@ package android.animation { method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...); method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...); method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...); method public void setAutoCancel(boolean); method public void setProperty(android.util.Property); method public void setPropertyName(java.lang.String); } core/java/android/animation/ObjectAnimator.java +70 −3 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.animation; import android.util.Log; import android.util.Property; import java.lang.reflect.Method; import java.util.ArrayList; /** Loading Loading @@ -49,6 +48,8 @@ public final class ObjectAnimator extends ValueAnimator { private Property mProperty; private boolean mAutoCancel = false; /** * Sets the name of the property that will be animated. This name is used to derive * a setter function that will be called to set animated values. Loading Loading @@ -348,15 +349,81 @@ public final class ObjectAnimator extends ValueAnimator { if (mProperty != null) { setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values)); } else { setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values)); setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator) null, values)); } } else { super.setObjectValues(values); } } /** * autoCancel controls whether an ObjectAnimator will be canceled automatically * when any other ObjectAnimator with the same target and properties is started. * Setting this flag may make it easier to run different animators on the same target * object without having to keep track of whether there are conflicting animators that * need to be manually canceled. Canceling animators must have the same exact set of * target properties, in the same order. * * @param cancel Whether future ObjectAnimators with the same target and properties * as this ObjectAnimator will cause this ObjectAnimator to be canceled. */ public void setAutoCancel(boolean cancel) { mAutoCancel = cancel; } private boolean hasSameTargetAndProperties(Animator anim) { if (anim instanceof ObjectAnimator) { PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues(); if (((ObjectAnimator) anim).getTarget() == mTarget && mValues.length == theirValues.length) { for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvhMine = mValues[i]; PropertyValuesHolder pvhTheirs = theirValues[i]; if (pvhMine.getPropertyName() == null || !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) { return false; } } return true; } } return false; } @Override public void start() { // See if any of the current active/pending animators need to be canceled AnimationHandler handler = sAnimationHandler.get(); if (handler != null) { int numAnims = handler.mAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mPendingAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mDelayedAnims.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } } if (DBG) { Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { Loading core/java/android/animation/ValueAnimator.java +13 −5 Original line number Diff line number Diff line Loading @@ -83,7 +83,10 @@ public class ValueAnimator extends Animator { // The static sAnimationHandler processes the internal timing loop on which all animations // are based private static ThreadLocal<AnimationHandler> sAnimationHandler = /** * @hide */ protected static ThreadLocal<AnimationHandler> sAnimationHandler = new ThreadLocal<AnimationHandler>(); // The time interpolator to be used if none is set on the animation Loading Loading @@ -531,22 +534,27 @@ public class ValueAnimator extends Animator { * animations possible. * * The handler uses the Choreographer for executing periodic callbacks. * * @hide */ private static class AnimationHandler implements Runnable { protected static class AnimationHandler implements Runnable { // The per-thread list of all active animations private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>(); /** @hide */ protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>(); // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>(); // The per-thread set of animations to be started on the next animation frame private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>(); /** @hide */ protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>(); /** * Internal per-thread collections used to avoid set collisions as animations start and end * while being processed. * @hide */ private final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>(); protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>(); private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>(); private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>(); Loading core/tests/coretests/src/android/animation/AutoCancelTest.java 0 → 100644 +201 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 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.animation; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import java.util.HashMap; import java.util.concurrent.TimeUnit; public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { boolean mAnimX1Canceled = false; boolean mAnimXY1Canceled = false; boolean mAnimX2Canceled = false; boolean mAnimXY2Canceled = false; private static final long START_DELAY = 100; private static final long DELAYED_START_DURATION = 200; private static final long FUTURE_TIMEOUT = 1000; HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>(); public AutoCancelTest() { super(BasicAnimatorActivity.class); } ObjectAnimator setupAnimator(long startDelay, String... properties) { ObjectAnimator returnVal; if (properties.length == 1) { returnVal = ObjectAnimator.ofFloat(this, properties[0], 0, 1); } else { PropertyValuesHolder[] pvhArray = new PropertyValuesHolder[properties.length]; for (int i = 0; i < properties.length; i++) { pvhArray[i] = PropertyValuesHolder.ofFloat(properties[i], 0, 1); } returnVal = ObjectAnimator.ofPropertyValuesHolder(this, pvhArray); } returnVal.setAutoCancel(true); returnVal.setStartDelay(startDelay); returnVal.addListener(mCanceledListener); return returnVal; } private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) throws Exception { // Animators to be auto-canceled final ObjectAnimator animX1 = setupAnimator(startDelay, "x"); final ObjectAnimator animY1 = setupAnimator(startDelay, "y"); final ObjectAnimator animXY1 = setupAnimator(startDelay, "x", "y"); final ObjectAnimator animXZ1 = setupAnimator(startDelay, "x", "z"); animX1.start(); animY1.start(); animXY1.start(); animXZ1.start(); final ObjectAnimator animX2 = setupAnimator(0, "x"); animX2.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // We expect only animX1 to be canceled at this point if (mCanceledMap.get(animX1) == null || mCanceledMap.get(animX1) != true || mCanceledMap.get(animY1) != null || mCanceledMap.get(animXY1) != null || mCanceledMap.get(animXZ1) != null) { future.set(false); } } }); final ObjectAnimator animXY2 = setupAnimator(0, "x", "y"); animXY2.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // We expect only animXY1 to be canceled at this point if (mCanceledMap.get(animXY1) == null || mCanceledMap.get(animXY1) != true || mCanceledMap.get(animY1) != null || mCanceledMap.get(animXZ1) != null) { future.set(false); } } @Override public void onAnimationEnd(Animator animation) { // Release future if not done already via failures during start future.release(); } }); if (startLater) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { animX2.start(); animXY2.start(); } }, DELAYED_START_DURATION); } else { animX2.start(); animXY2.start(); } } @SmallTest public void testAutoCancel() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(0, false, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelDelayed() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(START_DELAY, false, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelTestLater() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(0, true, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelDelayedTestLater() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(START_DELAY, true, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } private AnimatorListenerAdapter mCanceledListener = new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { mCanceledMap.put(animation, true); } }; public void setX(float x) {} public void setY(float y) {} public void setZ(float z) {} } core/tests/coretests/src/android/animation/FutureWaiter.java +9 −2 Original line number Diff line number Diff line Loading @@ -23,14 +23,21 @@ import com.google.common.util.concurrent.AbstractFuture; * {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It * also exposes the protected {@link AbstractFuture#setException(Throwable)} method. */ public class FutureWaiter extends AbstractFuture<Void> { public class FutureWaiter extends AbstractFuture<Boolean> { /** * Release the Future currently waiting on * {@link com.google.common.util.concurrent.AbstractFuture#get()}. */ public void release() { super.set(null); super.set(true); } /** * Used to indicate failure (when the result value is false). */ public void set(boolean result) { super.set(result); } @Override Loading Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -2445,6 +2445,7 @@ package android.animation { method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...); method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...); method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...); method public void setAutoCancel(boolean); method public void setProperty(android.util.Property); method public void setPropertyName(java.lang.String); }
core/java/android/animation/ObjectAnimator.java +70 −3 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.animation; import android.util.Log; import android.util.Property; import java.lang.reflect.Method; import java.util.ArrayList; /** Loading Loading @@ -49,6 +48,8 @@ public final class ObjectAnimator extends ValueAnimator { private Property mProperty; private boolean mAutoCancel = false; /** * Sets the name of the property that will be animated. This name is used to derive * a setter function that will be called to set animated values. Loading Loading @@ -348,15 +349,81 @@ public final class ObjectAnimator extends ValueAnimator { if (mProperty != null) { setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values)); } else { setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values)); setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator) null, values)); } } else { super.setObjectValues(values); } } /** * autoCancel controls whether an ObjectAnimator will be canceled automatically * when any other ObjectAnimator with the same target and properties is started. * Setting this flag may make it easier to run different animators on the same target * object without having to keep track of whether there are conflicting animators that * need to be manually canceled. Canceling animators must have the same exact set of * target properties, in the same order. * * @param cancel Whether future ObjectAnimators with the same target and properties * as this ObjectAnimator will cause this ObjectAnimator to be canceled. */ public void setAutoCancel(boolean cancel) { mAutoCancel = cancel; } private boolean hasSameTargetAndProperties(Animator anim) { if (anim instanceof ObjectAnimator) { PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues(); if (((ObjectAnimator) anim).getTarget() == mTarget && mValues.length == theirValues.length) { for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvhMine = mValues[i]; PropertyValuesHolder pvhTheirs = theirValues[i]; if (pvhMine.getPropertyName() == null || !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) { return false; } } return true; } } return false; } @Override public void start() { // See if any of the current active/pending animators need to be canceled AnimationHandler handler = sAnimationHandler.get(); if (handler != null) { int numAnims = handler.mAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mPendingAnimations.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mDelayedAnims.size(); for (int i = numAnims - 1; i >= 0; i--) { if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } } if (DBG) { Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { Loading
core/java/android/animation/ValueAnimator.java +13 −5 Original line number Diff line number Diff line Loading @@ -83,7 +83,10 @@ public class ValueAnimator extends Animator { // The static sAnimationHandler processes the internal timing loop on which all animations // are based private static ThreadLocal<AnimationHandler> sAnimationHandler = /** * @hide */ protected static ThreadLocal<AnimationHandler> sAnimationHandler = new ThreadLocal<AnimationHandler>(); // The time interpolator to be used if none is set on the animation Loading Loading @@ -531,22 +534,27 @@ public class ValueAnimator extends Animator { * animations possible. * * The handler uses the Choreographer for executing periodic callbacks. * * @hide */ private static class AnimationHandler implements Runnable { protected static class AnimationHandler implements Runnable { // The per-thread list of all active animations private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>(); /** @hide */ protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>(); // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>(); // The per-thread set of animations to be started on the next animation frame private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>(); /** @hide */ protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>(); /** * Internal per-thread collections used to avoid set collisions as animations start and end * while being processed. * @hide */ private final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>(); protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>(); private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>(); private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>(); Loading
core/tests/coretests/src/android/animation/AutoCancelTest.java 0 → 100644 +201 −0 Original line number Diff line number Diff line /* * Copyright (C) 2013 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.animation; import android.os.Handler; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import java.util.HashMap; import java.util.concurrent.TimeUnit; public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { boolean mAnimX1Canceled = false; boolean mAnimXY1Canceled = false; boolean mAnimX2Canceled = false; boolean mAnimXY2Canceled = false; private static final long START_DELAY = 100; private static final long DELAYED_START_DURATION = 200; private static final long FUTURE_TIMEOUT = 1000; HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>(); public AutoCancelTest() { super(BasicAnimatorActivity.class); } ObjectAnimator setupAnimator(long startDelay, String... properties) { ObjectAnimator returnVal; if (properties.length == 1) { returnVal = ObjectAnimator.ofFloat(this, properties[0], 0, 1); } else { PropertyValuesHolder[] pvhArray = new PropertyValuesHolder[properties.length]; for (int i = 0; i < properties.length; i++) { pvhArray[i] = PropertyValuesHolder.ofFloat(properties[i], 0, 1); } returnVal = ObjectAnimator.ofPropertyValuesHolder(this, pvhArray); } returnVal.setAutoCancel(true); returnVal.setStartDelay(startDelay); returnVal.addListener(mCanceledListener); return returnVal; } private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) throws Exception { // Animators to be auto-canceled final ObjectAnimator animX1 = setupAnimator(startDelay, "x"); final ObjectAnimator animY1 = setupAnimator(startDelay, "y"); final ObjectAnimator animXY1 = setupAnimator(startDelay, "x", "y"); final ObjectAnimator animXZ1 = setupAnimator(startDelay, "x", "z"); animX1.start(); animY1.start(); animXY1.start(); animXZ1.start(); final ObjectAnimator animX2 = setupAnimator(0, "x"); animX2.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // We expect only animX1 to be canceled at this point if (mCanceledMap.get(animX1) == null || mCanceledMap.get(animX1) != true || mCanceledMap.get(animY1) != null || mCanceledMap.get(animXY1) != null || mCanceledMap.get(animXZ1) != null) { future.set(false); } } }); final ObjectAnimator animXY2 = setupAnimator(0, "x", "y"); animXY2.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { // We expect only animXY1 to be canceled at this point if (mCanceledMap.get(animXY1) == null || mCanceledMap.get(animXY1) != true || mCanceledMap.get(animY1) != null || mCanceledMap.get(animXZ1) != null) { future.set(false); } } @Override public void onAnimationEnd(Animator animation) { // Release future if not done already via failures during start future.release(); } }); if (startLater) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { animX2.start(); animXY2.start(); } }, DELAYED_START_DURATION); } else { animX2.start(); animXY2.start(); } } @SmallTest public void testAutoCancel() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(0, false, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelDelayed() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(START_DELAY, false, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelTestLater() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(0, true, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } @SmallTest public void testAutoCancelDelayedTestLater() throws Exception { final FutureWaiter future = new FutureWaiter(); getActivity().runOnUiThread(new Runnable() { @Override public void run() { try { setupAnimators(START_DELAY, true, future); } catch (Exception e) { future.setException(e); } } }); assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS)); } private AnimatorListenerAdapter mCanceledListener = new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { mCanceledMap.put(animation, true); } }; public void setX(float x) {} public void setY(float y) {} public void setZ(float z) {} }
core/tests/coretests/src/android/animation/FutureWaiter.java +9 −2 Original line number Diff line number Diff line Loading @@ -23,14 +23,21 @@ import com.google.common.util.concurrent.AbstractFuture; * {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It * also exposes the protected {@link AbstractFuture#setException(Throwable)} method. */ public class FutureWaiter extends AbstractFuture<Void> { public class FutureWaiter extends AbstractFuture<Boolean> { /** * Release the Future currently waiting on * {@link com.google.common.util.concurrent.AbstractFuture#get()}. */ public void release() { super.set(null); super.set(true); } /** * Used to indicate failure (when the result value is false). */ public void set(boolean result) { super.set(result); } @Override Loading