Merge "Don't bring the Task to front when launching placeholder in background" into tm-dev

This commit is contained in:
Chris Li
2022-05-19 08:55:49 +00:00
committed by Android (Google) Code Review
2 changed files with 94 additions and 52 deletions

View File

@@ -241,7 +241,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// launching to top. We allow split as primary for activity reparent because the // launching to top. We allow split as primary for activity reparent because the
// activity may be split as primary before it is reparented out. In that case, we want // activity may be split as primary before it is reparented out. In that case, we want
// to show it as primary again when it is reparented back. // to show it as primary again when it is reparented back.
if (!resolveActivityToContainer(activity, true /* canSplitAsPrimary */)) { if (!resolveActivityToContainer(activity, true /* isOnReparent */)) {
// When there is no embedding rule matched, try to place it in the top container // When there is no embedding rule matched, try to place it in the top container
// like a normal launch. // like a normal launch.
placeActivityInTopContainer(activity); placeActivityInTopContainer(activity);
@@ -353,21 +353,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
void onActivityCreated(@NonNull Activity launchedActivity) { void onActivityCreated(@NonNull Activity launchedActivity) {
// TODO(b/229680885): we don't support launching into primary yet because we want to always // TODO(b/229680885): we don't support launching into primary yet because we want to always
// launch the new activity on top. // launch the new activity on top.
resolveActivityToContainer(launchedActivity, false /* canSplitAsPrimary */); resolveActivityToContainer(launchedActivity, false /* isOnReparent */);
updateCallbackIfNecessary(); updateCallbackIfNecessary();
} }
/** /**
* Checks if the new added activity should be routed to a particular container. It can create a * Checks if the new added activity should be routed to a particular container. It can create a
* new container for the activity and a new split container if necessary. * new container for the activity and a new split container if necessary.
* @param launchedActivity the new launched activity. * @param activity the activity that is newly added to the Task.
* @param canSplitAsPrimary whether we can put the new launched activity into primary split. * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
* We only support to split as primary for reparented activity for now.
* @return {@code true} if the activity was placed in TaskFragment container. * @return {@code true} if the activity was placed in TaskFragment container.
*/ */
@VisibleForTesting @VisibleForTesting
boolean resolveActivityToContainer(@NonNull Activity launchedActivity, boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
boolean canSplitAsPrimary) { if (isInPictureInPicture(activity) || activity.isFinishing()) {
if (isInPictureInPicture(launchedActivity) || launchedActivity.isFinishing()) {
// We don't embed activity when it is in PIP, or finishing. Return true since we don't // We don't embed activity when it is in PIP, or finishing. Return true since we don't
// want any extra handling. // want any extra handling.
return true; return true;
@@ -385,33 +385,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/ */
// 1. Whether the new launched activity should always expand. // 1. Whether the new launched activity should always expand.
if (shouldExpand(launchedActivity, null /* intent */)) { if (shouldExpand(activity, null /* intent */)) {
expandActivity(launchedActivity); expandActivity(activity);
return true; return true;
} }
// 2. Whether the new launched activity should launch a placeholder. // 2. Whether the new launched activity should launch a placeholder.
if (launchPlaceholderIfNecessary(launchedActivity)) { if (launchPlaceholderIfNecessary(activity, !isOnReparent)) {
return true; return true;
} }
// 3. Whether the new launched activity has already been in a split with a rule matched. // 3. Whether the new launched activity has already been in a split with a rule matched.
if (isNewActivityInSplitWithRuleMatched(launchedActivity)) { if (isNewActivityInSplitWithRuleMatched(activity)) {
return true; return true;
} }
// 4. Whether the activity below (if any) should be split with the new launched activity. // 4. Whether the activity below (if any) should be split with the new launched activity.
final Activity activityBelow = findActivityBelow(launchedActivity); final Activity activityBelow = findActivityBelow(activity);
if (activityBelow == null) { if (activityBelow == null) {
// Can't find any activity below. // Can't find any activity below.
return false; return false;
} }
if (putActivitiesIntoSplitIfNecessary(activityBelow, launchedActivity)) { if (putActivitiesIntoSplitIfNecessary(activityBelow, activity)) {
// Have split rule of [ activityBelow | launchedActivity ]. // Have split rule of [ activityBelow | launchedActivity ].
return true; return true;
} }
if (canSplitAsPrimary if (isOnReparent && putActivitiesIntoSplitIfNecessary(activity, activityBelow)) {
&& putActivitiesIntoSplitIfNecessary(launchedActivity, activityBelow)) {
// Have split rule of [ launchedActivity | activityBelow]. // Have split rule of [ launchedActivity | activityBelow].
return true; return true;
} }
@@ -430,17 +429,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
? topSplit.getSecondaryContainer() ? topSplit.getSecondaryContainer()
: topSplit.getPrimaryContainer(); : topSplit.getPrimaryContainer();
final Activity otherTopActivity = otherTopContainer.getTopNonFinishingActivity(); final Activity otherTopActivity = otherTopContainer.getTopNonFinishingActivity();
if (otherTopActivity == null || otherTopActivity == launchedActivity) { if (otherTopActivity == null || otherTopActivity == activity) {
// Can't find the top activity on the other split TaskFragment. // Can't find the top activity on the other split TaskFragment.
return false; return false;
} }
if (putActivitiesIntoSplitIfNecessary(otherTopActivity, launchedActivity)) { if (putActivitiesIntoSplitIfNecessary(otherTopActivity, activity)) {
// Have split rule of [ otherTopActivity | launchedActivity ]. // Have split rule of [ otherTopActivity | launchedActivity ].
return true; return true;
} }
// Have split rule of [ launchedActivity | otherTopActivity]. // Have split rule of [ launchedActivity | otherTopActivity].
return canSplitAsPrimary return isOnReparent && putActivitiesIntoSplitIfNecessary(activity, otherTopActivity);
&& putActivitiesIntoSplitIfNecessary(launchedActivity, otherTopActivity);
} }
/** /**
@@ -603,7 +601,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
// Check if activity requires a placeholder // Check if activity requires a placeholder
launchPlaceholderIfNecessary(activity); launchPlaceholderIfNecessary(activity, false /* isOnCreated */);
} }
@VisibleForTesting @VisibleForTesting
@@ -1043,10 +1041,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false; return false;
} }
return launchPlaceholderIfNecessary(topActivity); return launchPlaceholderIfNecessary(topActivity, false /* isOnCreated */);
} }
boolean launchPlaceholderIfNecessary(@NonNull Activity activity) { boolean launchPlaceholderIfNecessary(@NonNull Activity activity, boolean isOnCreated) {
final TaskFragmentContainer container = getContainerWithActivity(activity); final TaskFragmentContainer container = getContainerWithActivity(activity);
// Don't launch placeholder if the container is occluded. // Don't launch placeholder if the container is occluded.
if (container != null && container != getTopActiveContainer(container.getTaskId())) { if (container != null && container != getTopActiveContainer(container.getTaskId())) {
@@ -1067,11 +1065,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
// TODO(b/190433398): Handle failed request // TODO(b/190433398): Handle failed request
startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null /* options */, final Bundle options = getPlaceholderOptions(activity, isOnCreated);
startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), options,
placeholderRule, null /* failureCallback */, true /* isPlaceholder */); placeholderRule, null /* failureCallback */, true /* isPlaceholder */);
return true; return true;
} }
/**
* Gets the activity options for starting the placeholder activity. In case the placeholder is
* launched when the Task is in the background, we don't want to bring the Task to the front.
* @param primaryActivity the primary activity to launch the placeholder from.
* @param isOnCreated whether this happens during the primary activity onCreated.
*/
@VisibleForTesting
@Nullable
Bundle getPlaceholderOptions(@NonNull Activity primaryActivity, boolean isOnCreated) {
// Setting avoid move to front will also skip the animation. We only want to do that when
// the Task is currently in background.
// Check if the primary is resumed or if this is called when the primary is onCreated
// (not resumed yet).
if (isOnCreated || primaryActivity.isResumed()) {
return null;
}
final ActivityOptions options = ActivityOptions.makeBasic();
options.setAvoidMoveToFront();
return options.toBundle();
}
@VisibleForTesting @VisibleForTesting
boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) { boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
if (!splitContainer.isPlaceholderContainer()) { if (!splitContainer.isPlaceholderContainer()) {

View File

@@ -43,6 +43,7 @@ import static org.mockito.Mockito.never;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
@@ -50,6 +51,7 @@ import android.content.res.Resources;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
@@ -219,7 +221,8 @@ public class SplitControllerTest {
spyOn(tf); spyOn(tf);
doReturn(mActivity).when(tf).getTopNonFinishingActivity(); doReturn(mActivity).when(tf).getTopNonFinishingActivity();
doReturn(true).when(tf).isEmpty(); doReturn(true).when(tf).isEmpty();
doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mActivity); doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mActivity,
false /* isOnCreated */);
doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any()); doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any());
mSplitController.updateContainer(mTransaction, tf); mSplitController.updateContainer(mTransaction, tf);
@@ -287,8 +290,7 @@ public class SplitControllerTest {
mSplitController.onActivityCreated(mActivity); mSplitController.onActivityCreated(mActivity);
// Disallow to split as primary because we want the new launch to be always on top. // Disallow to split as primary because we want the new launch to be always on top.
verify(mSplitController).resolveActivityToContainer(mActivity, verify(mSplitController).resolveActivityToContainer(mActivity, false /* isOnReparent */);
false /* canSplitAsPrimary */);
} }
@Test @Test
@@ -297,8 +299,7 @@ public class SplitControllerTest {
mActivity.getActivityToken()); mActivity.getActivityToken());
// Treated as on activity created, but allow to split as primary. // Treated as on activity created, but allow to split as primary.
verify(mSplitController).resolveActivityToContainer(mActivity, verify(mSplitController).resolveActivityToContainer(mActivity, true /* isOnReparent */);
true /* canSplitAsPrimary */);
// Try to place the activity to the top TaskFragment when there is no matched rule. // Try to place the activity to the top TaskFragment when there is no matched rule.
verify(mSplitController).placeActivityInTopContainer(mActivity); verify(mSplitController).placeActivityInTopContainer(mActivity);
} }
@@ -432,7 +433,7 @@ public class SplitControllerTest {
@Test @Test
public void testResolveActivityToContainer_noRuleMatched() { public void testResolveActivityToContainer_noRuleMatched() {
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt()); verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -444,7 +445,7 @@ public class SplitControllerTest {
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it. // When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity( final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity); mActivity);
@@ -461,7 +462,7 @@ public class SplitControllerTest {
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it. // When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID); final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitPresenter).expandTaskFragment(container.getTaskFragmentToken()); verify(mSplitPresenter).expandTaskFragment(container.getTaskFragmentToken());
@@ -475,7 +476,7 @@ public class SplitControllerTest {
final Activity activity = createMockActivity(); final Activity activity = createMockActivity();
addSplitTaskFragments(activity, mActivity); addSplitTaskFragments(activity, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity( final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity); mActivity);
@@ -492,11 +493,12 @@ public class SplitControllerTest {
// Launch placeholder if the activity is not in any TaskFragment. // Launch placeholder if the activity is not in any TaskFragment.
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT, verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
null /* activityOptions */, placeholderRule, true /* isPlaceholder */); mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
} }
@Test @Test
@@ -508,7 +510,7 @@ public class SplitControllerTest {
mSplitController.newContainer(mActivity, TASK_ID); mSplitController.newContainer(mActivity, TASK_ID);
mSplitController.newContainer(activity, TASK_ID); mSplitController.newContainer(activity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
@@ -524,11 +526,12 @@ public class SplitControllerTest {
// Launch placeholder if the activity is in the topmost expanded TaskFragment. // Launch placeholder if the activity is in the topmost expanded TaskFragment.
mSplitController.newContainer(mActivity, TASK_ID); mSplitController.newContainer(mActivity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT, verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
null /* activityOptions */, placeholderRule, true /* isPlaceholder */); mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
} }
@Test @Test
@@ -539,7 +542,7 @@ public class SplitControllerTest {
final Activity secondaryActivity = createMockActivity(); final Activity secondaryActivity = createMockActivity();
addSplitTaskFragments(mActivity, secondaryActivity); addSplitTaskFragments(mActivity, secondaryActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
@@ -556,11 +559,12 @@ public class SplitControllerTest {
final Activity primaryActivity = createMockActivity(); final Activity primaryActivity = createMockActivity();
addSplitTaskFragments(primaryActivity, mActivity); addSplitTaskFragments(primaryActivity, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT, verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
null /* activityOptions */, placeholderRule, true /* isPlaceholder */); mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
} }
@Test @Test
@@ -582,7 +586,7 @@ public class SplitControllerTest {
splitRule); splitRule);
clearInvocations(mSplitController); clearInvocations(mSplitController);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt()); verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -598,7 +602,7 @@ public class SplitControllerTest {
addSplitTaskFragments(primaryActivity, mActivity); addSplitTaskFragments(primaryActivity, mActivity);
clearInvocations(mSplitController); clearInvocations(mSplitController);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt()); verify(mSplitController, never()).newContainer(any(), any(), anyInt());
@@ -616,7 +620,7 @@ public class SplitControllerTest {
mSplitController.getContainerWithActivity(secondaryActivity) mSplitController.getContainerWithActivity(secondaryActivity)
.addPendingAppearedActivity(mActivity); .addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
} }
@@ -641,7 +645,7 @@ public class SplitControllerTest {
secondaryContainer, secondaryContainer,
placeholderRule); placeholderRule);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
} }
@@ -655,7 +659,7 @@ public class SplitControllerTest {
TASK_ID); TASK_ID);
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertTrue(result); assertTrue(result);
assertSplitPair(activityBelow, mActivity); assertSplitPair(activityBelow, mActivity);
@@ -671,14 +675,13 @@ public class SplitControllerTest {
TASK_ID); TASK_ID);
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mActivity, boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
assertEquals(container, mSplitController.getContainerWithActivity(mActivity)); assertEquals(container, mSplitController.getContainerWithActivity(mActivity));
// Allow to split as primary. // Allow to split as primary.
result = mSplitController.resolveActivityToContainer(mActivity, result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);
true /* canSplitAsPrimary */);
assertTrue(result); assertTrue(result);
assertSplitPair(mActivity, activityBelow); assertSplitPair(mActivity, activityBelow);
@@ -697,7 +700,7 @@ public class SplitControllerTest {
activityBelow); activityBelow);
secondaryContainer.addPendingAppearedActivity(mActivity); secondaryContainer.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity, final boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity( final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity); mActivity);
@@ -718,19 +721,38 @@ public class SplitControllerTest {
primaryActivity); primaryActivity);
primaryContainer.addPendingAppearedActivity(mActivity); primaryContainer.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mActivity, boolean result = mSplitController.resolveActivityToContainer(mActivity,
false /* canSplitAsPrimary */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
assertEquals(primaryContainer, mSplitController.getContainerWithActivity(mActivity)); assertEquals(primaryContainer, mSplitController.getContainerWithActivity(mActivity));
result = mSplitController.resolveActivityToContainer(mActivity, result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);
true /* canSplitAsPrimary */);
assertTrue(result); assertTrue(result);
assertSplitPair(mActivity, primaryActivity); assertSplitPair(mActivity, primaryActivity);
} }
@Test
public void testGetPlaceholderOptions() {
doReturn(true).when(mActivity).isResumed();
assertNull(mSplitController.getPlaceholderOptions(mActivity, false /* isOnCreated */));
doReturn(false).when(mActivity).isResumed();
assertNull(mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */));
// Launch placeholder without moving the Task to front if the Task is now in background (not
// resumed or onCreated).
final Bundle options = mSplitController.getPlaceholderOptions(mActivity,
false /* isOnCreated */);
assertNotNull(options);
final ActivityOptions activityOptions = new ActivityOptions(options);
assertTrue(activityOptions.getAvoidMoveToFront());
}
/** Creates a mock activity in the organizer process. */ /** Creates a mock activity in the organizer process. */
private Activity createMockActivity() { private Activity createMockActivity() {
final Activity activity = mock(Activity.class); final Activity activity = mock(Activity.class);