summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/window/TransitionInfo.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java179
5 files changed, 188 insertions, 58 deletions
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 1a2d202be934..5bfa3d759a66 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -55,6 +55,7 @@ import android.view.WindowManager;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Used to communicate information about what is changing during a transition to a TransitionPlayer.
@@ -610,7 +611,7 @@ public final class TransitionInfo implements Parcelable {
private final WindowContainerToken mContainer;
private WindowContainerToken mParent;
private WindowContainerToken mLastParent;
- private final SurfaceControl mLeash;
+ private SurfaceControl mLeash;
private @TransitionMode int mMode = TRANSIT_NONE;
private @ChangeFlags int mFlags = FLAG_NONE;
private final Rect mStartAbsBounds = new Rect();
@@ -697,6 +698,11 @@ public final class TransitionInfo implements Parcelable {
mLastParent = lastParent;
}
+ /** Sets the animation leash for controlling this change's container */
+ public void setLeash(@NonNull SurfaceControl leash) {
+ mLeash = Objects.requireNonNull(leash);
+ }
+
/** Sets the transition mode for this change */
public void setMode(@TransitionMode int mode) {
mMode = mode;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 64294c9dbdc6..beae96ec3f3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -646,11 +646,12 @@ public abstract class WMShellBaseModule {
@Provides
static KeyguardTransitionHandler provideKeyguardTransitionHandler(
ShellInit shellInit,
+ ShellController shellController,
Transitions transitions,
@ShellMainThread Handler mainHandler,
@ShellMainThread ShellExecutor mainExecutor) {
return new KeyguardTransitionHandler(
- shellInit, transitions, mainHandler, mainExecutor);
+ shellInit, shellController, transitions, mainHandler, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index dba7f4bdd78d..0890861596a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -50,6 +50,8 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
@@ -59,10 +61,12 @@ import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
*
* <p>This takes the highest priority.
*/
-public class KeyguardTransitionHandler implements Transitions.TransitionHandler {
+public class KeyguardTransitionHandler
+ implements Transitions.TransitionHandler, KeyguardChangeListener {
private static final String TAG = "KeyguardTransition";
private final Transitions mTransitions;
+ private final ShellController mShellController;
private final Handler mMainHandler;
private final ShellExecutor mMainExecutor;
@@ -81,6 +85,9 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
// transition.
private boolean mIsLaunchingActivityOverLockscreen;
+ // Last value reported by {@link KeyguardChangeListener}.
+ private boolean mKeyguardShowing = true;
+
private final class StartedTransition {
final TransitionInfo mInfo;
final SurfaceControl.Transaction mFinishT;
@@ -93,12 +100,15 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
mPlayer = player;
}
}
+
public KeyguardTransitionHandler(
@NonNull ShellInit shellInit,
+ @NonNull ShellController shellController,
@NonNull Transitions transitions,
@NonNull Handler mainHandler,
@NonNull ShellExecutor mainExecutor) {
mTransitions = transitions;
+ mShellController = shellController;
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
@@ -106,6 +116,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
private void onInit() {
mTransitions.addHandler(this);
+ mShellController.addKeyguardChangeListener(this);
}
/**
@@ -121,6 +132,16 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler
}
@Override
+ public void onKeyguardVisibilityChanged(
+ boolean visible, boolean occluded, boolean animatingDismiss) {
+ mKeyguardShowing = visible;
+ }
+
+ public boolean isKeyguardShowing() {
+ return mKeyguardShowing;
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index c20d23e4374e..271a3b26305d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -64,6 +64,7 @@ import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Handles the Recents (overview) animation. Only one of these can run at a time. A recents
@@ -130,21 +131,21 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
wct.sendPendingIntent(intent, fillIn, options);
final RecentsController controller = new RecentsController(listener);
RecentsMixedHandler mixer = null;
- Transitions.TransitionHandler mixedHandler = null;
+ Consumer<IBinder> setTransitionForMixer = null;
for (int i = 0; i < mMixers.size(); ++i) {
- mixedHandler = mMixers.get(i).handleRecentsRequest(wct);
- if (mixedHandler != null) {
+ setTransitionForMixer = mMixers.get(i).handleRecentsRequest(wct);
+ if (setTransitionForMixer != null) {
mixer = mMixers.get(i);
break;
}
}
final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
- mixedHandler == null ? this : mixedHandler);
+ mixer == null ? this : mixer);
for (int i = 0; i < mStateListeners.size(); i++) {
mStateListeners.get(i).onTransitionStarted(transition);
}
if (mixer != null) {
- mixer.setRecentsTransition(transition);
+ setTransitionForMixer.accept(transition);
}
if (transition != null) {
controller.setTransition(transition);
@@ -589,6 +590,13 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
cancel("transit_sleep");
return;
}
+ if (mKeyguardLocked) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: keyguard is locked", mInstanceId);
+ // We will not accept new changes if we are swiping over the keyguard.
+ cancel(true /* toHome */, false /* withScreenshots */, "keyguard_locked");
+ return;
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.merge", mInstanceId);
// Keep all tasks in one list because order matters.
@@ -1105,22 +1113,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
* An interface for a mixed handler to receive information about recents requests (since these
* come into this handler directly vs from WMCore request).
*/
- public interface RecentsMixedHandler {
+ public interface RecentsMixedHandler extends Transitions.TransitionHandler {
/**
* Called when a recents request comes in. The handler can add operations to outWCT. If
- * the handler wants to "accept" the transition, it should return itself; otherwise, it
- * should return `null`.
+ * the handler wants to "accept" the transition, it should return a Consumer accepting the
+ * IBinder for the transition. If not, it should return `null`.
*
* If a mixed-handler accepts this recents, it will be the de-facto handler for this
* transition and is required to call the associated {@link #startAnimation},
* {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
*/
- Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT);
-
- /**
- * Reports the transition token associated with the accepted recents request. If there was
- * a problem starting the request, this will be called with `null`.
- */
- void setRecentsTransition(@Nullable IBinder transition);
+ @Nullable
+ Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 918a5a4bd53e..ce7fef2d1fdf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -23,6 +23,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -37,11 +38,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.Pair;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.common.ProtoLog;
@@ -61,7 +64,9 @@ import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
+import java.util.Map;
import java.util.Optional;
+import java.util.function.Consumer;
/**
* A handler for dealing with transitions involving multiple other handlers. For example: an
@@ -79,7 +84,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
private UnfoldTransitionHandler mUnfoldHandler;
private ActivityEmbeddingController mActivityEmbeddingController;
- private static class MixedTransition {
+ private class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
/** Both the display and split-state (enter/exit) is changing */
@@ -94,14 +99,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
/** Keyguard exit/occlude/unocclude transition. */
static final int TYPE_KEYGUARD = 5;
+ /** Recents transition on top of the lock screen. */
+ static final int TYPE_RECENTS_DURING_KEYGUARD = 6;
+
/** Recents Transition while in desktop mode. */
- static final int TYPE_RECENTS_DURING_DESKTOP = 6;
+ static final int TYPE_RECENTS_DURING_DESKTOP = 7;
/** Fold/Unfold transition. */
- static final int TYPE_UNFOLD = 7;
+ static final int TYPE_UNFOLD = 8;
/** Enter pip from one of the Activity Embedding windows. */
- static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 8;
+ static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9;
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -117,7 +125,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
final IBinder mTransition;
Transitions.TransitionHandler mLeftoversHandler = null;
+ TransitionInfo mInfo = null;
WindowContainerTransaction mFinishWCT = null;
+ SurfaceControl.Transaction mFinishT = null;
+ Transitions.TransitionFinishCallback mFinishCB = null;
/**
* Whether the transition has request for remote transition while mLeftoversHandler
@@ -138,6 +149,37 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
mTransition = transition;
}
+ boolean startSubAnimation(Transitions.TransitionHandler handler, TransitionInfo info,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ if (mInfo != null) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "startSubAnimation #%d.%d", mInfo.getDebugId(), info.getDebugId());
+ }
+ mInFlightSubAnimations++;
+ if (!handler.startAnimation(
+ mTransition, info, startT, finishT, wct -> onSubAnimationFinished(info, wct))) {
+ mInFlightSubAnimations--;
+ return false;
+ }
+ return true;
+ }
+
+ void onSubAnimationFinished(TransitionInfo info, WindowContainerTransaction wct) {
+ mInFlightSubAnimations--;
+ if (mInfo != null) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "onSubAnimationFinished #%d.%d remaining=%d",
+ mInfo.getDebugId(), info.getDebugId(), mInFlightSubAnimations);
+ }
+
+ joinFinishArgs(wct);
+
+ if (mInFlightSubAnimations == 0) {
+ mActiveTransitions.remove(MixedTransition.this);
+ mFinishCB.onTransitionFinished(mFinishWCT);
+ }
+ }
+
void joinFinishArgs(WindowContainerTransaction wct) {
if (wct != null) {
if (mFinishWCT == null) {
@@ -271,39 +313,46 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
}
@Override
- public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
+ public Consumer<IBinder> handleRecentsRequest(WindowContainerTransaction outWCT) {
if (mRecentsHandler != null) {
if (mSplitHandler.isSplitScreenVisible()) {
- return this;
+ return this::setRecentsTransitionDuringSplit;
+ } else if (mKeyguardHandler.isKeyguardShowing()) {
+ return this::setRecentsTransitionDuringKeyguard;
} else if (mDesktopTasksController != null
// Check on the default display. Recents/gesture nav is only available there
&& mDesktopTasksController.getVisibleTaskCount(DEFAULT_DISPLAY) > 0) {
- return this;
+ return this::setRecentsTransitionDuringDesktop;
}
}
return null;
}
- @Override
- public void setRecentsTransition(IBinder transition) {
- if (mSplitHandler.isSplitScreenVisible()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
- + "Split-Screen is foreground, so treat it as Mixed.");
- final MixedTransition mixed = new MixedTransition(
- MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
- mixed.mLeftoversHandler = mRecentsHandler;
- mActiveTransitions.add(mixed);
- } else if (DesktopModeStatus.isEnabled()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
- + "desktop mode is active, so treat it as Mixed.");
- final MixedTransition mixed = new MixedTransition(
- MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition);
- mixed.mLeftoversHandler = mRecentsHandler;
- mActiveTransitions.add(mixed);
- } else {
- throw new IllegalStateException("Accepted a recents transition but don't know how to"
- + " handle it");
- }
+ private void setRecentsTransitionDuringSplit(IBinder transition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "Split-Screen is foreground, so treat it as Mixed.");
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
+ mixed.mLeftoversHandler = mRecentsHandler;
+ mActiveTransitions.add(mixed);
+ }
+
+ private void setRecentsTransitionDuringKeyguard(IBinder transition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "keyguard is visible, so treat it as Mixed.");
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_KEYGUARD, transition);
+ mixed.mLeftoversHandler = mRecentsHandler;
+ mActiveTransitions.add(mixed);
+ }
+
+ private void setRecentsTransitionDuringDesktop(IBinder transition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "desktop mode is active, so treat it as Mixed.");
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition);
+ mixed.mLeftoversHandler = mRecentsHandler;
+ mActiveTransitions.add(mixed);
}
private TransitionInfo subCopy(@NonNull TransitionInfo info,
@@ -410,6 +459,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
return animateKeyguard(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
+ return animateRecentsDuringKeyguard(mixed, info, startTransaction, finishTransaction,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction,
finishCallback);
@@ -764,24 +816,28 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- final Transitions.TransitionFinishCallback finishCB = (wct) -> {
- mixed.mInFlightSubAnimations--;
- if (mixed.mInFlightSubAnimations == 0) {
- mActiveTransitions.remove(mixed);
- finishCallback.onTransitionFinished(wct);
- }
- };
- mixed.mInFlightSubAnimations++;
+ if (mixed.mFinishT == null) {
+ mixed.mFinishT = finishTransaction;
+ mixed.mFinishCB = finishCallback;
+ }
// Sync pip state.
if (mPipHandler != null) {
mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
}
- if (!mKeyguardHandler.startAnimation(
- mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) {
- mixed.mInFlightSubAnimations--;
- return false;
+ return mixed.startSubAnimation(mKeyguardHandler, info, startTransaction, finishTransaction);
+ }
+
+ private boolean animateRecentsDuringKeyguard(@NonNull final MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mixed.mInfo == null) {
+ mixed.mInfo = info;
+ mixed.mFinishT = finishTransaction;
+ mixed.mFinishCB = finishCallback;
}
- return true;
+ return mixed.startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction);
}
private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed,
@@ -905,6 +961,15 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_KEYGUARD) {
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
+ handoverTransitionLeashes(mixed, info, t, mixed.mFinishT);
+ if (animateKeyguard(mixed, info, t, mixed.mFinishT, mixed.mFinishCB)) {
+ finishCallback.onTransitionFinished(null);
+ }
+ }
+ mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
finishCallback);
@@ -947,4 +1012,38 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
}
}
+
+ /**
+ * Update an incoming {@link TransitionInfo} with the leashes from an ongoing
+ * {@link MixedTransition} so that it can take over some parts of the animation without
+ * reparenting to new transition roots.
+ */
+ private static void handoverTransitionLeashes(@NonNull MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT) {
+
+ // Show the roots in case they contain new changes not present in the original transition.
+ for (int j = info.getRootCount() - 1; j >= 0; --j) {
+ startT.show(info.getRoot(j).getLeash());
+ }
+
+ // Find all of the leashes from the original transition.
+ Map<WindowContainerToken, TransitionInfo.Change> originalChanges = new ArrayMap<>();
+ for (TransitionInfo.Change oldChange : mixed.mInfo.getChanges()) {
+ if (oldChange.getContainer() != null) {
+ originalChanges.put(oldChange.getContainer(), oldChange);
+ }
+ }
+
+ // Merge the animation leashes by re-using the original ones if we see the same container
+ // in the new transition and the old.
+ for (TransitionInfo.Change newChange : info.getChanges()) {
+ if (originalChanges.containsKey(newChange.getContainer())) {
+ final TransitionInfo.Change oldChange = originalChanges.get(newChange.getContainer());
+ startT.reparent(newChange.getLeash(), null);
+ newChange.setLeash(oldChange.getLeash());
+ }
+ }
+ }
}