1 // Copyright 2013 Google Inc. All Rights Reserved.
2 
3 package com.android.cts.verifier;
4 
5 import android.app.AlertDialog;
6 import android.content.ActivityNotFoundException;
7 import android.content.Intent;
8 import android.os.Bundle;
9 import android.os.Parcel;
10 import android.os.Parcelable;
11 import android.util.Log;
12 import android.view.View;
13 import android.view.View.OnClickListener;
14 import android.view.ViewGroup;
15 import android.widget.Button;
16 import android.widget.TextView;
17 
18 /**
19  *  A generic activity for intent based tests.
20  *
21  *  This activity can be reused for various tests that are intent based. The activity consists of a
22  *  text view containing instructions and one or more buttons that trigger intents.
23  *  Each button can send one or more intenrs as startActivity() calls.
24  *
25  *  The intents can either be generated statically and passed as an extra to the intent that started
26  *  this activity or in some cases where the intent needs to be based on dynamic data (for example
27  *  time of day), an {@link IntentFactory} class name can be passed instead. This intent factory
28  *  will dynamically create the intent when the button is clicked based on the test id and the
29  *  button that was clicked.
30  */
31 public class IntentDrivenTestActivity extends PassFailButtons.Activity implements OnClickListener {
32     private static final String TAG = "IntentDrivenTestActivity";
33 
34     public static final String EXTRA_ID = "id";
35     public static final String EXTRA_TITLE = "title";
36     public static final String EXTRA_INFO = "info";
37     public static final String EXTRA_BUTTONS = "buttons";
38 
39     private String mTestId;
40     private ButtonInfo[] mButtonInfos;
41 
42 
43     @Override
onCreate(Bundle savedInstanceState)44     protected void onCreate(Bundle savedInstanceState) {
45         super.onCreate(savedInstanceState);
46         setContentView(R.layout.intent_driven_test);
47         setPassFailButtonClickListeners();
48 
49         final Intent intent = getIntent();
50         if (!intent.hasExtra(EXTRA_ID)
51                 || !intent.hasExtra(EXTRA_TITLE)
52                 || !intent.hasExtra(EXTRA_INFO)
53                 || !intent.hasExtra(EXTRA_BUTTONS)) {
54             throw new IllegalArgumentException(
55                     "Intent must have EXTRA_ID, EXTRA_TITLE, EXTRA_INFO & EXTRA_BUTTONS");
56         }
57 
58         mTestId = intent.getStringExtra(EXTRA_ID);
59         setTitle(intent.getIntExtra(EXTRA_TITLE, -1));
60 
61         final TextView info = (TextView) findViewById(R.id.info);
62         info.setText(intent.getIntExtra(EXTRA_INFO, -1));
63 
64         final ViewGroup buttons = (ViewGroup) findViewById(R.id.buttons);
65         final Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_BUTTONS);
66         mButtonInfos = new ButtonInfo[parcelables.length];
67         for (int i = 0; i < parcelables.length; i++) {
68             final ButtonInfo buttonInfo = (ButtonInfo) parcelables[i];
69             mButtonInfos[i] = buttonInfo;
70             final Button button = new Button(this);
71             buttons.addView(button);
72             button.setText(buttonInfo.mButtonText);
73             button.setTag(i);
74             button.setOnClickListener(this);
75         }
76     }
77 
78     @Override
onResume()79     protected void onResume() {
80         super.onResume();
81         setResult(RESULT_CANCELED);
82     }
83 
84     @Override
onClick(View v)85     public void onClick(View v) {
86         final ButtonInfo buttonInfo = mButtonInfos[(Integer) v.getTag()];
87         final Intent[] intents = buttonInfo.getIntents();
88         try {
89             if (intents != null) {
90                 for (Parcelable intent : intents) {
91                     startActivity((Intent) intent);
92                 }
93             } else {
94                 final Class<?> factoryClass;
95                 final String className = buttonInfo.getIntentFactoryClassName();
96                 try {
97                     factoryClass = Thread.currentThread().getContextClassLoader().loadClass(className);
98                 } catch (ClassNotFoundException e) {
99                     throw new IllegalArgumentException("Factory not found: " + className, e);
100                 }
101                 final IntentFactory factory;
102                 try {
103                     factory = (IntentFactory) factoryClass.newInstance();
104                 } catch (InstantiationException e) {
105                     throw new IllegalArgumentException("Can't create factory: " + className, e);
106                 } catch (IllegalAccessException e) {
107                     throw new IllegalArgumentException("Can't create factory: " + className, e);
108                 }
109 
110                 for (Intent intent : factory.createIntents(mTestId, buttonInfo.getButtonText())) {
111                     startActivity(intent);
112                 }
113             }
114         } catch (ActivityNotFoundException e) {
115             // Instead of crashing, log and alert the user of that the Intent couldn't be resolved.
116             Log.w(TAG, "Could not resolve Intent.", e);
117             showFailedIntentResolution(e.getMessage());
118         }
119     }
120 
showFailedIntentResolution(String message)121     private void showFailedIntentResolution(String message) {
122         new AlertDialog.Builder(this)
123                 .setTitle(R.string.intent_not_resolved)
124                 .setMessage(getString(R.string.intent_not_resolved_info, message))
125                 .show();
126     }
127 
128     @Override
getTestId()129     public String getTestId() {
130         return mTestId;
131     }
132 
133     public static class TestInfo {
134         private final String mTestId;
135         private final int mTitle;
136         private final int mInfoText;
137         private final ButtonInfo[] mButtons;
138 
TestInfo(String testId, int title, int infoText, ButtonInfo... buttons)139         public TestInfo(String testId,  int title, int infoText, ButtonInfo... buttons) {
140             /** The Intent Driven Test layout {@link R.layout.intent_driven_test} is designed for
141              *  up to 2 buttons and won't render well with more buttons on small screen devices.
142              *  If you need more than 2 buttons, please change the layout so it can properly render
143              *  them even on the smallest devices.
144              */
145             if (buttons.length > 2) {
146                 throw new RuntimeException("Too many buttons");
147             }
148             mTestId = testId;
149             mTitle = title;
150             mInfoText = infoText;
151             mButtons = buttons;
152         }
153 
getTestId()154         public String getTestId() {
155             return mTestId;
156         }
157 
getTitle()158         public int getTitle() {
159             return mTitle;
160         }
161 
getInfoText()162         public int getInfoText() {
163             return mInfoText;
164         }
165 
getButtons()166         public ButtonInfo[] getButtons() {
167             return mButtons;
168         }
169     }
170 
171     public static class ButtonInfo implements Parcelable {
172         private final int mButtonText;
173         private final Intent[] mIntents;
174         private final String mIntentFactoryClassName;
175 
ButtonInfo(int buttonText, Intent... intents)176         public ButtonInfo(int buttonText, Intent... intents) {
177             mButtonText = buttonText;
178             mIntents = intents;
179             mIntentFactoryClassName = null;
180         }
181 
ButtonInfo(int buttonText, String intentFactoryClassName)182         public ButtonInfo(int buttonText, String intentFactoryClassName) {
183             mButtonText = buttonText;
184             mIntents = null;
185             mIntentFactoryClassName = intentFactoryClassName;
186         }
187 
ButtonInfo(Parcel source)188         public ButtonInfo(Parcel source) {
189             mButtonText = source.readInt();
190             final Parcelable[] parcelables = source.readParcelableArray(null);
191             if (parcelables != null) {
192                 mIntents = new Intent[parcelables.length];
193                 for (int i = 0, parcelablesLength = parcelables.length; i < parcelablesLength; i++) {
194                     mIntents[i] = (Intent) parcelables[i];
195                 }
196             } else {
197                 mIntents = null;
198             }
199             mIntentFactoryClassName = source.readString();
200         }
201 
getButtonText()202         public int getButtonText() {
203             return mButtonText;
204         }
205 
getIntents()206         public Intent[] getIntents() {
207             return mIntents;
208         }
209 
getIntentFactoryClassName()210         public String getIntentFactoryClassName() {
211             return mIntentFactoryClassName;
212         }
213 
214         @Override
describeContents()215         public int describeContents() {
216             return 0;
217         }
218 
219         @Override
writeToParcel(Parcel dest, int flags)220         public void writeToParcel(Parcel dest, int flags) {
221             dest.writeInt(mButtonText);
222             dest.writeParcelableArray(mIntents, 0);
223             dest.writeString(mIntentFactoryClassName);
224         }
225 
226         public static final Creator<ButtonInfo> CREATOR = new Creator<ButtonInfo>() {
227             public ButtonInfo createFromParcel(Parcel source) {
228                 return new ButtonInfo(source);
229             }
230 
231             public ButtonInfo[] newArray(int size) {
232                 return new ButtonInfo[size];
233             }
234         };
235 
236     }
237 
238     public interface IntentFactory {
createIntents(String testId, int buttonText)239         Intent[] createIntents(String testId, int buttonText);
240     }
241 }
242