1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.intentplayground;
18 
19 import static java.util.Collections.singletonList;
20 
21 import android.app.TaskStackBuilder;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.util.Log;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.LinkedList;
29 import java.util.List;
30 
31 /**
32  * TestBase holds methods to query, test and compare task hierarchies.
33  */
34 public class TestBase {
35     static final String TAG = "TestBase";
36     private List<TaskStackBuilder> mBuilders;
37     private Context mContext;
38 
TestBase(Context context, Node hierarchy)39     TestBase(Context context, Node hierarchy) {
40         mBuilders = new LinkedList<>();
41         mContext = context;
42         setActivities(hierarchy);
43     }
44 
45     /**
46      * Launch the activities specified by the constructor.
47      *
48      * @param style An enum that chooses which method to use to launch the activities.
49      */
startActivities(LaunchStyle style)50     void startActivities(LaunchStyle style) {
51         switch (style) {
52             // COMMAND_LINE will only work if the application is installed with system permissions
53             // that allow it to use am shell command "am start ..."
54             case COMMAND_LINE:
55                 mBuilders.forEach(tsb -> Arrays.stream(tsb.getIntents())
56                         .forEach(AMControl::launchInBackground));
57                 break;
58             case TASK_STACK_BUILDER:
59                 mBuilders.forEach(tsb -> {
60                     // TODO: does this indicate bug in ActivityManager?
61                     // The launch of each activity needs to be delayed a bit or ActivityManager will7
62                     // skip creating most of them
63                     try {
64                         Thread.sleep(500);
65                         tsb.startActivities();
66                         Thread.sleep(500);
67                     } catch (InterruptedException ie) {
68                         Log.e(TAG, ie.getMessage());
69                     }
70                 });
71                 break;
72             case LAUNCH_FORWARD:
73                 mBuilders.forEach(tsb -> {
74                     // The launch of each activity needs to be delayed a bit or ActivityManager will
75                     // skip creating most of them
76                     try {
77                         Thread.sleep(500);
78                     } catch (InterruptedException ie) {
79                         Log.e(TAG, ie.getMessage());
80                     }
81                     ArrayList<Intent> nextIntents = new ArrayList<>(Arrays.asList(
82                             tsb.getIntents()));
83                     Intent launch = nextIntents.remove(0)
84                             .putParcelableArrayListExtra(BaseActivity.EXTRA_LAUNCH_FORWARD,
85                                     nextIntents);
86                     if (BuildConfig.DEBUG) {
87                         Log.d(TAG, "Launching " + launch.getComponent().toString());
88                     }
89                     mContext.startActivity(launch);
90                 });
91                 break;
92         }
93     }
94 
setActivities(Node hierarchy)95     void setActivities(Node hierarchy) {
96         // Build list of TaskStackBuilders from task hierarchy modeled by Node
97         if (hierarchy.mChildren.isEmpty()) return;
98         mBuilders.clear();
99         hierarchy.mChildren.forEach(taskParent -> {
100             TaskStackBuilder tb = TaskStackBuilder.create(mContext);
101             Intent taskRoot = new Intent()
102                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
103                     .setComponent(taskParent.mChildren.get(0).mName);
104             tb.addNextIntent(taskRoot);
105             taskParent.mChildren.subList(1, taskParent.mChildren.size()).forEach(activity ->
106                     tb.addNextIntent(new Intent().setComponent(activity.mName)));
107             mBuilders.add(tb);
108         });
109         // Edit the mIntent of the last activity in the last task so that it will relaunch the
110         // activity that constructed this TestBase
111         TaskStackBuilder tsb = mBuilders.get(mBuilders.size() - 1);
112         Intent lastIntent = tsb.editIntentAt(tsb.getIntentCount() - 1);
113         Intent launcherIntent =  new Intent(mContext, mContext.getClass());
114         lastIntent.putParcelableArrayListExtra(BaseActivity.EXTRA_LAUNCH_FORWARD,
115                 new ArrayList<>(singletonList(launcherIntent)));
116     }
117 
getContext()118     public Context getContext() { return mContext; }
119 
120     /**
121      * An enum representing options for launching a series of tasks using this TestBase.
122      */
123     enum LaunchStyle { TASK_STACK_BUILDER, COMMAND_LINE, LAUNCH_FORWARD}
124 }
125 
126