summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-12-03 11:35:29 -0800
committerXavier Ducrohet <xav@android.com>2010-12-03 11:50:36 -0800
commite1179ea065392485a6e3bf1e28a1242179cd48b0 (patch)
tree2dfb7425ad0a3eccb6cbe75e4bfa761e80e9db47 /tools
parent01811aa86279af1b341a4fff344d66c0ebdd63da (diff)
downloadframeworks_base-e1179ea065392485a6e3bf1e28a1242179cd48b0.zip
frameworks_base-e1179ea065392485a6e3bf1e28a1242179cd48b0.tar.gz
frameworks_base-e1179ea065392485a6e3bf1e28a1242179cd48b0.tar.bz2
LayoutLib: Animation support in insert/move/removeChild actions.
Also update to use the new SceneResult API. Change-Id: Iaac6df0c250fbefc8758310c37e0cf47cae6875d
Diffstat (limited to 'tools')
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java51
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java251
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java48
4 files changed, 288 insertions, 64 deletions
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 7aa0e3c..2a94774 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -335,7 +335,7 @@ public final class Bridge extends LayoutBridge {
t2 = t.getCause();
}
return new BridgeLayoutScene(null,
- new SceneResult(SceneStatus.ERROR_UNKNOWN, t2.getMessage(), t2));
+ SceneStatus.ERROR_UNKNOWN.getResult(t2.getMessage(), t2));
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
index 2b9d52f..400a05f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/AnimationThread.java
@@ -22,7 +22,6 @@ import com.android.layoutlib.api.LayoutScene.IAnimationListener;
import com.android.layoutlib.api.SceneResult.SceneStatus;
import com.android.layoutlib.bridge.Bridge;
-import android.animation.Animator;
import android.animation.ValueAnimator;
import android.os.Handler;
import android.os.Handler_Delegate;
@@ -32,7 +31,19 @@ import android.os.Handler_Delegate.IHandlerCallback;
import java.util.LinkedList;
import java.util.Queue;
-public class AnimationThread extends Thread {
+/**
+ * Abstract animation thread.
+ * <p/>
+ * This does not actually start an animation, instead it fakes a looper that will play whatever
+ * animation is sending messages to its own {@link Handler}.
+ * <p/>
+ * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
+ * <p/>
+ * If {@link #preAnimation()} does not start an animation something then the thread doesn't do
+ * anything.
+ *
+ */
+public abstract class AnimationThread extends Thread {
private static class MessageBundle {
final Handler mTarget;
@@ -47,17 +58,19 @@ public class AnimationThread extends Thread {
}
private final LayoutSceneImpl mScene;
- private final Animator mAnimator;
Queue<MessageBundle> mQueue = new LinkedList<MessageBundle>();
private final IAnimationListener mListener;
- public AnimationThread(LayoutSceneImpl scene, Animator animator, IAnimationListener listener) {
+ public AnimationThread(LayoutSceneImpl scene, String threadName, IAnimationListener listener) {
+ super(threadName);
mScene = scene;
- mAnimator = animator;
mListener = listener;
}
+ public abstract SceneResult preAnimation();
+ public abstract void postAnimation();
+
@Override
public void run() {
Bridge.prepareThread();
@@ -73,13 +86,20 @@ public class AnimationThread extends Thread {
}
});
- // start the animation. This will send a message to the handler right away, so
- // mQueue is filled when this method returns.
- mAnimator.start();
+ // call out to the pre-animation work, which should start an animation or more.
+ SceneResult result = preAnimation();
+ if (result.isSuccess() == false) {
+ mListener.done(result);
+ }
// loop the animation
LayoutScene scene = mScene.getScene();
do {
+ // check early.
+ if (mListener.isCanceled()) {
+ break;
+ }
+
// get the next message.
MessageBundle bundle = mQueue.poll();
if (bundle == null) {
@@ -97,8 +117,13 @@ public class AnimationThread extends Thread {
}
}
+ // check after sleeping.
+ if (mListener.isCanceled()) {
+ break;
+ }
+
// ready to do the work, acquire the scene.
- SceneResult result = mScene.acquire(250);
+ result = mScene.acquire(250);
if (result.isSuccess() == false) {
mListener.done(result);
return;
@@ -107,6 +132,11 @@ public class AnimationThread extends Thread {
// process the bundle. If the animation is not finished, this will enqueue
// the next message, so mQueue will have another one.
try {
+ // check after acquiring in case it took a while.
+ if (mListener.isCanceled()) {
+ break;
+ }
+
bundle.mTarget.handleMessage(bundle.mMessage);
if (mScene.render().isSuccess()) {
mListener.onNewFrame(scene);
@@ -114,10 +144,11 @@ public class AnimationThread extends Thread {
} finally {
mScene.release();
}
- } while (mQueue.size() > 0);
+ } while (mListener.isCanceled() == false && mQueue.size() > 0);
mListener.done(SceneStatus.SUCCESS.getResult());
} finally {
+ postAnimation();
Handler_Delegate.setCallback(null);
Bridge.cleanupThread();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
index 3c8e8cd..359180f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java
@@ -43,8 +43,9 @@ import com.android.layoutlib.bridge.android.BridgeWindow;
import com.android.layoutlib.bridge.android.BridgeWindowSession;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import android.animation.Animator;
import android.animation.AnimatorInflater;
-import android.animation.ObjectAnimator;
+import android.animation.LayoutTransition;
import android.app.Fragment_Delegate;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -56,7 +57,6 @@ import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
@@ -346,7 +346,7 @@ public class LayoutSceneImpl {
return SceneStatus.SUCCESS.getResult();
} catch (PostInflateException e) {
- return new SceneResult(SceneStatus.ERROR_INFLATION, e.getMessage(), e);
+ return SceneStatus.ERROR_INFLATION.getResult(e.getMessage(), e);
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -357,7 +357,7 @@ public class LayoutSceneImpl {
// log it
mParams.getLogger().error(t);
- return new SceneResult(SceneStatus.ERROR_INFLATION, t.getMessage(), t);
+ return SceneStatus.ERROR_INFLATION.getResult(t.getMessage(), t);
}
}
@@ -370,13 +370,14 @@ public class LayoutSceneImpl {
* the scene, or if {@link #acquire(long)} was not called.
*
* @see SceneParams#getRenderingMode()
+ * @see LayoutScene#render(long)
*/
public SceneResult render() {
checkLock();
try {
if (mViewRoot == null) {
- return new SceneResult(SceneStatus.ERROR_NOT_INFLATED);
+ return SceneStatus.ERROR_NOT_INFLATED.getResult();
}
// measure the views
int w_spec, h_spec;
@@ -482,7 +483,7 @@ public class LayoutSceneImpl {
// log it
mParams.getLogger().error(t);
- return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
+ return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
}
}
@@ -493,6 +494,8 @@ public class LayoutSceneImpl {
*
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see LayoutScene#animate(Object, String, boolean, IAnimationListener)
*/
public SceneResult animate(Object targetObject, String animationName,
boolean isFrameworkAnimation, IAnimationListener listener) {
@@ -515,12 +518,11 @@ public class LayoutSceneImpl {
if (animationResource != null) {
try {
- ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(
- mContext, animationId);
+ Animator anim = AnimatorInflater.loadAnimator(mContext, animationId);
if (anim != null) {
anim.setTarget(targetObject);
- new AnimationThread(this, anim, listener).start();
+ new PlayAnimationThread(anim, this, animationName, listener).start();
return SceneStatus.SUCCESS.getResult();
}
@@ -531,15 +533,25 @@ public class LayoutSceneImpl {
t = t.getCause();
}
- return new SceneResult(SceneStatus.ERROR_UNKNOWN, t.getMessage(), t);
+ return SceneStatus.ERROR_UNKNOWN.getResult(t.getMessage(), t);
}
}
- return new SceneResult(SceneStatus.ERROR_ANIM_NOT_FOUND);
+ return SceneStatus.ERROR_ANIM_NOT_FOUND.getResult();
}
- public SceneResult insertChild(ViewGroup parentView, IXmlPullParser childXml,
- int index, IAnimationListener listener) {
+ /**
+ * Insert a new child into an existing parent.
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see LayoutScene#insertChild(Object, IXmlPullParser, int, IAnimationListener)
+ */
+ public SceneResult insertChild(final ViewGroup parentView, IXmlPullParser childXml,
+ final int index, IAnimationListener listener) {
checkLock();
// create a block parser for the XML
@@ -549,84 +561,217 @@ public class LayoutSceneImpl {
// inflate the child without adding it to the root since we want to control where it'll
// get added. We do pass the parentView however to ensure that the layoutParams will
// be created correctly.
- View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
+ final View child = mInflater.inflate(blockParser, parentView, false /*attachToRoot*/);
- // add it to the parentView in the correct location
- try {
- parentView.addView(child, index);
- } catch (UnsupportedOperationException e) {
- // looks like this is a view class that doesn't support children manipulation!
- return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ invalidateRenderingSize();
+
+ if (listener != null) {
+ new AnimationThread(this, "insertChild", listener) {
+
+ @Override
+ public SceneResult preAnimation() {
+ parentView.setLayoutTransition(new LayoutTransition());
+ return addView(parentView, child, index);
+ }
+
+ @Override
+ public void postAnimation() {
+ parentView.setLayoutTransition(null);
+ }
+ }.start();
+
+ // always return success since the real status will come through the listener.
+ return SceneStatus.SUCCESS.getResult(child);
}
- invalidateRenderingSize();
+ // add it to the parentView in the correct location
+ SceneResult result = addView(parentView, child, index);
+ if (result.isSuccess() == false) {
+ return result;
+ }
- SceneResult result = render();
+ result = render();
if (result.isSuccess()) {
- result.setData(child);
+ result = result.getCopyWithData(child);
}
return result;
}
- public SceneResult moveChild(ViewGroup parentView, View childView, int index,
+ /**
+ * Adds a given view to a given parent at a given index.
+ *
+ * @param parent the parent to receive the view
+ * @param view the view to add to the parent
+ * @param index the index where to do the add.
+ *
+ * @return a SceneResult with {@link SceneStatus#SUCCESS} or
+ * {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+ * adding views.
+ */
+ private SceneResult addView(ViewGroup parent, View view, int index) {
+ try {
+ parent.addView(view, index);
+ return SceneStatus.SUCCESS.getResult();
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
+ }
+ }
+
+ /**
+ * Moves a view to a new parent at a given location
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see LayoutScene#moveChild(Object, Object, int, Map, IAnimationListener)
+ */
+ public SceneResult moveChild(final ViewGroup parentView, final View childView, final int index,
Map<String, String> layoutParamsMap, IAnimationListener listener) {
checkLock();
+ invalidateRenderingSize();
+
LayoutParams layoutParams = null;
- try {
- ViewParent parent = childView.getParent();
- if (parent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) parent;
- parentGroup.removeView(childView);
- }
+ if (layoutParamsMap != null) {
+ // need to create a new LayoutParams object for the new parent.
+ layoutParams = parentView.generateLayoutParams(
+ new BridgeLayoutParamsMapAttributes(layoutParamsMap));
+ }
- // add it to the parentView in the correct location
+ if (listener != null) {
+ final LayoutParams params = layoutParams;
+ new AnimationThread(this, "moveChild", listener) {
- if (layoutParamsMap != null) {
- // need to create a new LayoutParams object for the new parent.
- layoutParams = parentView.generateLayoutParams(
- new BridgeLayoutParamsMapAttributes(layoutParamsMap));
+ @Override
+ public SceneResult preAnimation() {
+ parentView.setLayoutTransition(new LayoutTransition());
+ return moveView(parentView, childView, index, params);
+ }
- parentView.addView(childView, index, layoutParams);
- } else {
- parentView.addView(childView, index);
- }
- } catch (UnsupportedOperationException e) {
- // looks like this is a view class that doesn't support children manipulation!
- return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ @Override
+ public void postAnimation() {
+ parentView.setLayoutTransition(null);
+ }
+ }.start();
+
+ // always return success since the real status will come through the listener.
+ return SceneStatus.SUCCESS.getResult(layoutParams);
}
- invalidateRenderingSize();
+ SceneResult result = moveView(parentView, childView, index, layoutParams);
+ if (result.isSuccess() == false) {
+ return result;
+ }
- SceneResult result = render();
+ result = render();
if (layoutParams != null && result.isSuccess()) {
- result.setData(layoutParams);
+ result = result.getCopyWithData(layoutParams);
}
return result;
}
- public SceneResult removeChild(View childView, IAnimationListener listener) {
- checkLock();
-
+ /**
+ * Moves a View from its current parent to a new given parent at a new given location, with
+ * an optional new {@link LayoutParams} instance
+ *
+ * @param parent the new parent
+ * @param view the view to move
+ * @param index the new location in the new parent
+ * @param params an option (can be null) {@link LayoutParams} instance.
+ *
+ * @return a SceneResult with {@link SceneStatus#SUCCESS} or
+ * {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+ * adding views.
+ */
+ private SceneResult moveView(ViewGroup parent, View view, int index, LayoutParams params) {
try {
- ViewParent parent = childView.getParent();
- if (parent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) parent;
- parentGroup.removeView(childView);
+ ViewGroup previousParent = (ViewGroup) view.getParent();
+ previousParent.removeView(view);
+
+ // add it to the parentView in the correct location
+
+ if (params != null) {
+ parent.addView(view, index, params);
+ } else {
+ parent.addView(view, index);
}
+
+ return SceneStatus.SUCCESS.getResult();
} catch (UnsupportedOperationException e) {
// looks like this is a view class that doesn't support children manipulation!
- return new SceneResult(SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN);
+ return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
}
+ }
+
+ /**
+ * Removes a child from its current parent.
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see LayoutScene#removeChild(Object, IAnimationListener)
+ */
+ public SceneResult removeChild(final View childView, IAnimationListener listener) {
+ checkLock();
invalidateRenderingSize();
+ final ViewGroup parent = (ViewGroup) childView.getParent();
+
+ if (listener != null) {
+ new AnimationThread(this, "moveChild", listener) {
+
+ @Override
+ public SceneResult preAnimation() {
+ parent.setLayoutTransition(new LayoutTransition());
+ return removeView(parent, childView);
+ }
+
+ @Override
+ public void postAnimation() {
+ parent.setLayoutTransition(null);
+ }
+ }.start();
+
+ // always return success since the real status will come through the listener.
+ return SceneStatus.SUCCESS.getResult();
+ }
+
+ SceneResult result = removeView(parent, childView);
+ if (result.isSuccess() == false) {
+ return result;
+ }
+
return render();
}
/**
+ * Removes a given view from its current parent.
+ *
+ * @param view the view to remove from its parent
+ *
+ * @return a SceneResult with {@link SceneStatus#SUCCESS} or
+ * {@link SceneStatus#ERROR_VIEWGROUP_NO_CHILDREN} if the given parent doesn't support
+ * adding views.
+ */
+ private SceneResult removeView(ViewGroup parent, View view) {
+ try {
+ parent.removeView(view);
+ return SceneStatus.SUCCESS.getResult();
+ } catch (UnsupportedOperationException e) {
+ // looks like this is a view class that doesn't support children manipulation!
+ return SceneStatus.ERROR_VIEWGROUP_NO_CHILDREN.getResult();
+ }
+ }
+
+ /**
* Checks that the lock is owned by the current thread and that the current context is the one
* from this scene.
*
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
new file mode 100644
index 0000000..bbc7f4b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 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 com.android.layoutlib.bridge.impl;
+
+import com.android.layoutlib.api.SceneResult;
+import com.android.layoutlib.api.LayoutScene.IAnimationListener;
+import com.android.layoutlib.api.SceneResult.SceneStatus;
+
+import android.animation.Animator;
+
+public class PlayAnimationThread extends AnimationThread {
+
+ private final Animator mAnimator;
+
+ public PlayAnimationThread(Animator animator, LayoutSceneImpl scene, String animName,
+ IAnimationListener listener) {
+ super(scene, animName, listener);
+ mAnimator = animator;
+ }
+
+ @Override
+ public SceneResult preAnimation() {
+ // start the animation. This will send a message to the handler right away, so
+ // the queue is filled when this method returns.
+ mAnimator.start();
+
+ return SceneStatus.SUCCESS.getResult();
+ }
+
+ @Override
+ public void postAnimation() {
+ // nothing to be done.
+ }
+}