blob: a5cce2a8ce65d09044931c8265321a404c4e28bc [file] [log] [blame]
/*
* Copyright (C) 2019 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.server.wm.intent;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.server.wm.intent.LaunchSequence.intent;
import static android.server.wm.intent.LaunchSequence.intentForResult;
import static android.server.wm.intent.Persistence.flag;
import android.content.Intent;
import android.server.wm.intent.Activities.RegularActivity;
import android.server.wm.intent.Persistence.IntentFlag;
import android.server.wm.intent.Persistence.LaunchIntent;
import com.google.common.collect.Lists;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Contains all information to create and reuse intent test launches.
* It enumerates all the flags so they can easily be used.
*
* It also stores commonly used {@link LaunchSequence} objects for reuse.
*/
public class Cases {
public static final IntentFlag CLEAR_TASK = flag(FLAG_ACTIVITY_CLEAR_TASK,
"FLAG_ACTIVITY_CLEAR_TASK");
public static final IntentFlag CLEAR_TOP = flag(FLAG_ACTIVITY_CLEAR_TOP,
"FLAG_ACTIVITY_CLEAR_TOP");
private static final IntentFlag SINGLE_TOP = flag(FLAG_ACTIVITY_SINGLE_TOP,
"FLAG_ACTIVITY_SINGLE_TOP");
public static final IntentFlag NEW_TASK = flag(FLAG_ACTIVITY_NEW_TASK,
"FLAG_ACTIVITY_NEW_TASK");
public static final IntentFlag NEW_DOCUMENT = flag(FLAG_ACTIVITY_NEW_DOCUMENT,
"FLAG_ACTIVITY_NEW_DOCUMENT");
private static final IntentFlag MULTIPLE_TASK = flag(FLAG_ACTIVITY_MULTIPLE_TASK,
"FLAG_ACTIVITY_MULTIPLE_TASK");
public static final IntentFlag RESET_TASK_IF_NEEDED = flag(
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,
"FLAG_ACTIVITY_RESET_TASK_IF_NEEDED");
public static final IntentFlag PREVIOUS_IS_TOP = flag(FLAG_ACTIVITY_PREVIOUS_IS_TOP,
"FLAG_ACTIVITY_PREVIOUS_IS_TOP");
public static final IntentFlag REORDER_TO_FRONT = flag(FLAG_ACTIVITY_REORDER_TO_FRONT,
"FLAG_ACTIVITY_REORDER_TO_FRONT");
public static final IntentFlag NO_HISTORY = flag(FLAG_ACTIVITY_NO_HISTORY,
"FLAG_ACTIVITY_NO_HISTORY");
// Flag only used for parsing intents that contain no flags.
private static final IntentFlag NONE = flag(0, "");
public final List<IntentFlag> flags = Lists.newArrayList(
CLEAR_TASK,
CLEAR_TOP,
SINGLE_TOP,
NEW_TASK,
NEW_DOCUMENT,
MULTIPLE_TASK,
RESET_TASK_IF_NEEDED,
PREVIOUS_IS_TOP,
REORDER_TO_FRONT,
NO_HISTORY
);
// Definition of intents used across multiple test cases.
private final LaunchIntent mRegularIntent = intent(RegularActivity.class);
private final LaunchIntent mSingleTopIntent = intent(Activities.SingleTopActivity.class);
private final LaunchIntent mAff1Intent = intent(Activities.TaskAffinity1Activity.class);
private final LaunchIntent mSecondAff1Intent = intent(Activities.TaskAffinity1Activity2.class);
private final LaunchIntent mSingleInstanceIntent = intent(
Activities.SingleInstanceActivity.class);
private final LaunchIntent mSingleTaskIntent = intent(Activities.SingleTaskActivity.class);
private final LaunchIntent mRegularForResultIntent = intentForResult(RegularActivity.class);
// LaunchSequences used across multiple test cases.
private final LaunchSequence mRegularSequence = LaunchSequence.create(mRegularIntent);
// To show that the most recent task get's resumed by new task
private final LaunchSequence mTwoAffinitiesSequence =
LaunchSequence.create(mAff1Intent)
.append(mRegularIntent)
.append(mAff1Intent.withFlags(NEW_TASK, MULTIPLE_TASK));
// Used to show that the task affinity is determined by the activity that started it,
// Not the affinity of the current root activity.
private final LaunchSequence mRearrangedRootSequence = LaunchSequence.create(mRegularIntent)
.append(mAff1Intent.withFlags(NEW_TASK))
.append(mRegularIntent)
.append(mAff1Intent.withFlags(REORDER_TO_FRONT));
private final LaunchSequence mSingleInstanceActivitySequence =
LaunchSequence.create(mSingleInstanceIntent);
private final LaunchSequence mSingleTaskActivitySequence = LaunchSequence.create(
mSingleTaskIntent);
public List<LaunchSequence> newTaskCases() {
LaunchIntent aff1NewTask = mAff1Intent.withFlags(NEW_TASK);
return Lists.newArrayList(
// 1. Single instance will start a new task even without new task set
mRegularSequence.act(mSingleInstanceIntent),
// 2. With new task it will still end up in a new task
mRegularSequence.act(mSingleInstanceIntent.withFlags(NEW_TASK)),
// 3. Starting an activity with a different affinity without new task will put it in
// the existing task
mRegularSequence.act(mAff1Intent),
// 4. Starting a task with a different affinity and new task will start a new task
mRegularSequence.act(aff1NewTask),
// 5. Starting the intent with new task will not add a new activity to the task
mRegularSequence.act(mRegularIntent.withFlags(NEW_TASK)),
// 6. A different activity with the same affinity will start a new activity in
// the sam task
mRegularSequence.act(mSingleTopIntent.withFlags(NEW_TASK)),
// 7. To show that the most recent task get's resumed by new task, this can't
// be observed without differences in the same activity / task
mTwoAffinitiesSequence.act(aff1NewTask),
// 8. To show that new task respects the root as a single even
// if it is not at the bottom
mRearrangedRootSequence.act(mAff1Intent.withFlags(NEW_TASK)),
// 9. To show that new task with non root does start a new activity.
mRearrangedRootSequence.act(mSecondAff1Intent.withFlags(NEW_TASK)),
// 10. Multiple task will allow starting activities of the same affinity in
// different tasks
mRegularSequence.act(mRegularIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
// 11. Single instance will not start a new task even with multiple task on
mSingleInstanceActivitySequence.act(
mSingleInstanceIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
// 12. The same should happen for single task.
mSingleTaskActivitySequence.act(
mSingleTaskIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
// 13. This starts a regular in a new task
mSingleInstanceActivitySequence.act(mRegularIntent),
// 14. This adds regular in the same task
mSingleTaskActivitySequence.act(mRegularIntent),
// 15. Starting the activity for result keeps it in the same task
mSingleInstanceActivitySequence.act(mRegularForResultIntent),
// 16. Restarts the previous task with regular activity.
mRegularSequence.append(mSingleInstanceIntent).act(mRegularIntent)
);
}
/**
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT }test cases are the same as
* {@link Cases#newTaskCases()}, we should check them for differences.
*/
public List<LaunchSequence> newDocumentCases() {
LaunchIntent aff1NewDocument = mAff1Intent.withFlags(NEW_DOCUMENT);
return Lists.newArrayList(
// 1. Single instance will start a new task even without new task set
mRegularSequence.act(mSingleInstanceIntent),
// 2. With new task it will still end up in a new task
mRegularSequence.act(mSingleInstanceIntent.withFlags(NEW_DOCUMENT)),
// 3. Starting an activity with a different affinity without new task will put it
// in the existing task
mRegularSequence.act(mAff1Intent),
// 4. With new document it will start it's own task
mRegularSequence.act(aff1NewDocument),
// 5. Starting the intent with new task will not add a new activity to the task
mRegularSequence.act(mRegularIntent.withFlags(NEW_DOCUMENT)),
// 6. Unlike the case with NEW_TASK, with new Document
mRegularSequence.act(mSingleTopIntent.withFlags(NEW_DOCUMENT)),
// 7. To show that the most recent task get's resumed by new task
mTwoAffinitiesSequence.act(aff1NewDocument),
// 8. To show that new task respects the root as a single
mRearrangedRootSequence.act(mAff1Intent.withFlags(NEW_DOCUMENT)),
// 9. New document starts a third task here, because there was no task for the
// document yet
mRearrangedRootSequence.act(mSecondAff1Intent.withFlags(NEW_DOCUMENT)),
// 10. Multiple task wil allow starting activities of the same affinity in different
// tasks
mRegularSequence.act(mRegularIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK)),
// 11. Single instance will not start a new task even with multiple task on
mSingleInstanceActivitySequence
.act(mSingleInstanceIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK)),
// 12. The same should happen for single task.
mSingleTaskActivitySequence.act(
mSingleTaskIntent.withFlags(NEW_DOCUMENT, MULTIPLE_TASK))
);
}
public List<LaunchSequence> clearCases() {
LaunchSequence doubleRegularActivity = mRegularSequence.append(mRegularIntent);
return Lists.newArrayList(
// 1. This will clear the bottom and end up with just one activity
mRegularSequence.act(mRegularIntent.withFlags(CLEAR_TOP)),
// 2. This will result in still two regulars
doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP)),
// 3. This will result in a single regular it clears the top regular
// activity and then fails to start a new regular activity because it is already
// at the root of the task
doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, NEW_TASK)),
// 3. This will also result in two regulars, showing the first difference between
// new document and new task.
doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, NEW_DOCUMENT)),
// 4. This is here to show that previous is top has no effect on clear top or single
// top
doubleRegularActivity.act(mRegularIntent.withFlags(CLEAR_TOP, PREVIOUS_IS_TOP)),
// 5. Clear top finds the same activity in the task and clears from there
mRegularSequence.append(mAff1Intent).append(mRegularIntent).act(
mAff1Intent.withFlags(CLEAR_TOP))
);
}
/**
* Tests for {@link android.app.Activity#startActivityForResult(Intent, int)}
*/
//TODO: If b/122968776 is fixed these tests need to be updated
public List<LaunchSequence> forResultCases() {
LaunchIntent singleTopForResult = intentForResult(Activities.SingleTopActivity.class);
return Lists.newArrayList(
// 1. Start just a single regular activity
mRegularSequence.act(mRegularIntent.withFlags(SINGLE_TOP)),
// 2. For result will start a second activity
mRegularSequence.act(mRegularForResultIntent.withFlags(SINGLE_TOP)),
// 3. The same but for SINGLE_TOP as a launch mode
LaunchSequence.create(mSingleTopIntent).act(mSingleTopIntent),
// 4. Launch mode SINGLE_TOP when launched for result also starts a second activity.
LaunchSequence.create(mSingleTopIntent).act(singleTopForResult),
// 5. CLEAR_TOP results in a single regular activity
mRegularSequence.act(mRegularIntent.withFlags(CLEAR_TOP)),
// 6. Clear will still kill the for result
mRegularSequence.act(mRegularForResultIntent.withFlags(CLEAR_TOP)),
// 7. An activity started for result can go to a different task
mRegularSequence.act(mRegularForResultIntent.withFlags(NEW_TASK, MULTIPLE_TASK)),
// 8. Reorder to front with for result
mRegularSequence.append(mAff1Intent).act(
mRegularForResultIntent.withFlags(REORDER_TO_FRONT)),
// 9. Reorder can move an activity above one that it started for result
mRegularSequence.append(intentForResult(Activities.TaskAffinity1Activity.class))
.act(mRegularIntent.withFlags(REORDER_TO_FRONT))
);
}
/**
* Reset task if needed will trigger when it is delivered with new task set
* and there are activities in the task that have a different affinity.
*
* @return the test cases
*/
//TODO: If b/122324373 is fixed these test need to be updated.
public List<LaunchSequence> resetTaskIfNeeded() {
// If a task with a different affinity like this get's reset
// it will create another task in the same stack with the now orphaned activity.
LaunchIntent resetRegularTask = mRegularIntent.withFlags(NEW_TASK,
RESET_TASK_IF_NEEDED);
LaunchSequence resetIfNeeded = mRegularSequence.append(mAff1Intent)
.act(resetRegularTask);
// If you try to reset a task with an activity that was started for result
// it will not move task.
LaunchSequence resetWontMoveResult = mRegularSequence.append(
intentForResult(Activities.TaskAffinity1Activity.class))
.act(resetRegularTask);
// Reset will not move activities with to a task with that affinity,
// instead it will always create a new task in that stack.
LaunchSequence resetToExistingTask2 = mRegularSequence
.append(mAff1Intent)
.append(mAff1Intent.withFlags(NEW_TASK))
.act(resetRegularTask);
// If a reset occurs the activities that move retain the same order
// in the new task as they had in the old task.
LaunchSequence resetOrdering = mRegularSequence
.append(mAff1Intent)
.append(mSecondAff1Intent)
.act(resetRegularTask);
return Lists.newArrayList(resetIfNeeded, resetWontMoveResult, resetToExistingTask2,
resetOrdering);
}
/**
* The human readable flags in the JSON files need to be converted back to the corresponding
* IntentFlag object when reading the file. This creates a map from the flags to their
* corresponding object.
*
* @return lookup table for the parsing of intent flags in the json files.
*/
public Map<String, IntentFlag> createFlagParsingTable() {
HashMap<String, IntentFlag> flags = new HashMap<>();
for (IntentFlag flag : this.flags) {
flags.put(flag.name, flag);
}
flags.put(NONE.getName(), NONE);
return flags;
}
}