diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java new file mode 100644 index 0000000000000..4cd2c504c83e5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2021 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.wm.shell.transition; + +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.os.IBinder; +import android.util.ArrayMap; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; + +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TransactionPool; + +import java.util.ArrayList; + +/** The default handler that handles anything not already handled. */ +public class DefaultTransitionHandler implements Transitions.TransitionHandler { + private final TransactionPool mTransactionPool; + private final ShellExecutor mMainExecutor; + private final ShellExecutor mAnimExecutor; + + /** Keeps track of the currently-running animations associated with each transition. */ + private final ArrayMap> mAnimations = new ArrayMap<>(); + + DefaultTransitionHandler(@NonNull TransactionPool transactionPool, + @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { + mTransactionPool = transactionPool; + mMainExecutor = mainExecutor; + mAnimExecutor = animExecutor; + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + if (mAnimations.containsKey(transition)) { + throw new IllegalStateException("Got a duplicate startAnimation call for " + + transition); + } + final ArrayList animations = new ArrayList<>(); + mAnimations.put(transition, animations); + final boolean isOpening = Transitions.isOpeningType(info.getType()); + + final Runnable onAnimFinish = () -> { + if (!animations.isEmpty()) return; + mAnimations.remove(transition); + finishCallback.run(); + }; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + + // Don't animate anything with an animating parent + if (change.getParent() != null) continue; + + final int mode = change.getMode(); + if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { + if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + continue; + } + // fade in + startExampleAnimation( + animations, change.getLeash(), true /* show */, onAnimFinish); + } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { + // fade out + startExampleAnimation( + animations, change.getLeash(), false /* show */, onAnimFinish); + } + } + t.apply(); + // run finish now in-case there are no animations + onAnimFinish.run(); + return true; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, + @Nullable ActivityManager.RunningTaskInfo triggerTask) { + return null; + } + + // TODO(shell-transitions): real animations + private void startExampleAnimation(@NonNull ArrayList animations, + @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) { + final float end = show ? 1.f : 0.f; + final float start = 1.f - end; + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + final ValueAnimator va = ValueAnimator.ofFloat(start, end); + va.setDuration(500); + va.addUpdateListener(animation -> { + float fraction = animation.getAnimatedFraction(); + transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); + transaction.apply(); + }); + final Runnable finisher = () -> { + transaction.setAlpha(leash, end); + transaction.apply(); + mTransactionPool.release(transaction); + mMainExecutor.execute(() -> { + animations.remove(va); + finishCallback.run(); + }); + }; + va.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { } + + @Override + public void onAnimationEnd(Animator animation) { + finisher.run(); + } + + @Override + public void onAnimationCancel(Animator animation) { + finisher.run(); + } + + @Override + public void onAnimationRepeat(Animator animation) { } + }); + animations.add(va); + mAnimExecutor.execute(va::start); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 7ce71b0a11589..3b2ac70007e45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -23,8 +23,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; -import android.animation.Animator; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -41,6 +39,7 @@ import android.window.WindowOrganizer; import androidx.annotation.BinderThread; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; @@ -48,6 +47,7 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; +import java.util.Arrays; /** Plays transition animations */ public class Transitions { @@ -58,7 +58,6 @@ public class Transitions { SystemProperties.getBoolean("persist.debug.shell_transit", false); private final WindowOrganizer mOrganizer; - private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; @@ -67,7 +66,6 @@ public class Transitions { private final ArrayList mHandlers = new ArrayList<>(); private static final class ActiveTransition { - ArrayList mAnimations = null; TransitionHandler mFirstHandler = null; } @@ -77,10 +75,11 @@ public class Transitions { public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mOrganizer = organizer; - mTransactionPool = pool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mPlayerImpl = new TransitionPlayerImpl(); + // The very last handler (0 in the list) should be the default one. + mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor)); } /** Register this transition handler with Core */ @@ -104,47 +103,10 @@ public class Transitions { return mAnimExecutor; } - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull IBinder transition, @NonNull SurfaceControl leash, - boolean show) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); - mTransactionPool.release(transaction); - mMainExecutor.execute(() -> { - mActiveTransitions.get(transition).mAnimations.remove(va); - onFinish(transition); - }); - }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationRepeat(Animator animation) { } - }); - mActiveTransitions.get(transition).mAnimations.add(va); - mAnimExecutor.execute(va::start); + /** Only use this in tests. This is used to avoid running animations during tests. */ + @VisibleForTesting + void replaceDefaultHandlerForTest(TransitionHandler handler) { + mHandlers.set(0, handler); } /** @return true if the transition was triggered by opening something vs closing something */ @@ -217,18 +179,16 @@ public class Transitions { } } - private void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, + @VisibleForTesting + void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); final ActiveTransition active = mActiveTransitions.get(transitionToken); if (active == null) { throw new IllegalStateException("Got transitionReady for non-active transition " - + transitionToken + ". expecting one of " + mActiveTransitions.keySet()); - } - if (active.mAnimations != null) { - throw new IllegalStateException("Got a duplicate onTransitionReady call for " - + transitionToken); + + transitionToken + ". expecting one of " + + Arrays.toString(mActiveTransitions.keySet().toArray())); } if (!info.getRootLeash().isValid()) { // Invalid root-leash implies that the transition is empty/no-op, so just do @@ -253,44 +213,21 @@ public class Transitions { return; } } - - // No handler chose to perform this animation, so fall-back to the - // default animation handling. - final boolean isOpening = isOpeningType(info.getType()); - active.mAnimations = new ArrayList<>(); // Play fade animations - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - - // Don't animate anything with an animating parent - if (change.getParent() != null) continue; - - final int mode = info.getChanges().get(i).getMode(); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - // This received a transferred starting window, so don't animate - continue; - } - // fade in - startExampleAnimation(transitionToken, change.getLeash(), true /* show */); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - startExampleAnimation(transitionToken, change.getLeash(), false /* show */); - } - } - t.apply(); - onFinish(transitionToken); + throw new IllegalStateException( + "This shouldn't happen, maybe the default handler is broken."); } private void onFinish(IBinder transition) { - final ActiveTransition active = mActiveTransitions.get(transition); - if (active.mAnimations != null && !active.mAnimations.isEmpty()) return; + if (!mActiveTransitions.containsKey(transition)) { + throw new IllegalStateException("Trying to finish an already-finished transition."); + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animations finished, notifying core %s", transition); mActiveTransitions.remove(transition); mOrganizer.finishTransition(transition, null, null); } - private void requestStartTransition(int type, @NonNull IBinder transitionToken, + void requestStartTransition(int type, @NonNull IBinder transitionToken, @Nullable ActivityManager.RunningTaskInfo triggerTask) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: type=%d %s", type, transitionToken); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java new file mode 100644 index 0000000000000..88b8498f27123 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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.wm.shell; + +import com.android.wm.shell.common.ShellExecutor; + +import java.util.ArrayList; + +/** + * Really basic test executor. It just gathers all events in a blob. The only option is to + * execute everything at once. If better control over delayed execution is needed, please add it. + */ +public class TestShellExecutor implements ShellExecutor { + final ArrayList mRunnables = new ArrayList<>(); + + @Override + public void execute(Runnable runnable) { + mRunnables.add(runnable); + } + + @Override + public void executeDelayed(Runnable r, long delayMillis) { + mRunnables.add(r); + } + + @Override + public void removeCallbacks(Runnable r) { + mRunnables.remove(r); + } + + public void flushAll() { + for (int i = mRunnables.size() - 1; i >= 0; --i) { + mRunnables.get(i).run(); + } + mRunnables.clear(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java new file mode 100644 index 0000000000000..c46e59ad396a0 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2021 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.wm.shell.transition; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OPEN; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager.RunningTaskInfo; +import android.os.Binder; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.WindowContainerTransaction; +import android.window.WindowOrganizer; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TransactionPool; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +/** + * Tests for the shell transitions. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ShellTransitionTests { + + private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); + private final TransactionPool mTransactionPool = mock(TransactionPool.class); + private final TestShellExecutor mMainExecutor = new TestShellExecutor(); + private final ShellExecutor mAnimExecutor = new TestShellExecutor(); + private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); + + @Before + public void setUp() { + doAnswer(invocation -> invocation.getArguments()[1]) + .when(mOrganizer).startTransition(anyInt(), any(), any()); + } + + @Test + public void testBasicTransitionFlow() { + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, + mAnimExecutor); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + IBinder transitToken = new Binder(); + transitions.requestStartTransition(TRANSIT_OPEN, transitToken, null /* trigger */); + verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any()); + TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class)); + assertEquals(1, mDefaultHandler.activeCount()); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any()); + } + + @Test + public void testNonDefaultHandler() { + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, + mAnimExecutor); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + + final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); + // Make a test handler that only responds to multi-window triggers AND only animates + // Change transitions. + TestTransitionHandler testHandler = new TestTransitionHandler() { + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + for (TransitionInfo.Change chg : info.getChanges()) { + if (chg.getMode() == TRANSIT_CHANGE) { + return super.startAnimation(transition, info, t, finishCallback); + } + } + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, + @Nullable RunningTaskInfo triggerTask) { + return (triggerTask != null + && triggerTask.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) + ? handlerWCT : null; + } + }; + transitions.addHandler(testHandler); + + IBinder transitToken = new Binder(); + TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); + + // Make a request that will be rejected by the testhandler. + transitions.requestStartTransition(TRANSIT_OPEN, transitToken, null /* trigger */); + verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull()); + transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class)); + assertEquals(1, mDefaultHandler.activeCount()); + assertEquals(0, testHandler.activeCount()); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + + // Make a request that will be handled by testhandler but not animated by it. + RunningTaskInfo mwTaskInfo = + createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + transitions.requestStartTransition(TRANSIT_OPEN, transitToken, mwTaskInfo); + verify(mOrganizer, times(1)).startTransition( + eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT)); + transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class)); + assertEquals(1, mDefaultHandler.activeCount()); + assertEquals(0, testHandler.activeCount()); + mDefaultHandler.finishAll(); + mMainExecutor.flushAll(); + + // Make a request that will be handled AND animated by testhandler. + // Add an aggressive handler (doesn't handle but always animates) on top to make sure that + // the test handler gets first shot at animating since it claimed to handle it. + TestTransitionHandler topHandler = new TestTransitionHandler(); + transitions.addHandler(topHandler); + transitions.requestStartTransition(TRANSIT_CHANGE, transitToken, mwTaskInfo); + verify(mOrganizer, times(1)).startTransition( + eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT)); + TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) + .addChange(TRANSIT_CHANGE).build(); + transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class)); + assertEquals(0, mDefaultHandler.activeCount()); + assertEquals(1, testHandler.activeCount()); + assertEquals(0, topHandler.activeCount()); + testHandler.finishAll(); + mMainExecutor.flushAll(); + } + + class TransitionInfoBuilder { + final TransitionInfo mInfo; + + TransitionInfoBuilder(@WindowManager.TransitionType int type) { + mInfo = new TransitionInfo(type, 0 /* flags */); + mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0); + } + + TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode, + RunningTaskInfo taskInfo) { + final TransitionInfo.Change change = + new TransitionInfo.Change(null /* token */, null /* leash */); + change.setMode(mode); + change.setTaskInfo(taskInfo); + mInfo.addChange(change); + return this; + } + + TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode) { + return addChange(mode, null /* taskInfo */); + } + + TransitionInfo build() { + return mInfo; + } + } + + class TestTransitionHandler implements Transitions.TransitionHandler { + final ArrayList mFinishes = new ArrayList<>(); + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull Runnable finishCallback) { + mFinishes.add(finishCallback); + return true; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(int type, @NonNull IBinder transition, + @Nullable RunningTaskInfo triggerTask) { + return null; + } + + void finishAll() { + for (int i = mFinishes.size() - 1; i >= 0; --i) { + mFinishes.get(i).run(); + } + mFinishes.clear(); + } + + int activeCount() { + return mFinishes.size(); + } + } + + private static SurfaceControl createMockSurface(boolean valid) { + SurfaceControl sc = mock(SurfaceControl.class); + doReturn(valid).when(sc).isValid(); + return sc; + } + + private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, int activityType) { + RunningTaskInfo taskInfo = new RunningTaskInfo(); + taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); + taskInfo.configuration.windowConfiguration.setActivityType(activityType); + return taskInfo; + } + +}