1 /* 2 * Copyright (C) 2011 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.android.cts.verifier; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ActivityInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.ResolveInfo; 24 import android.os.Bundle; 25 import android.widget.ListView; 26 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.Comparator; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Map; 34 35 /** 36 * {@link TestListAdapter} that populates the {@link TestListActivity}'s {@link ListView} by 37 * reading data from the CTS Verifier's AndroidManifest.xml. 38 * <p> 39 * Making a new test activity to appear in the list requires the following steps: 40 * 41 * <ol> 42 * <li>REQUIRED: Add an activity to the AndroidManifest.xml with an intent filter with a 43 * main action and the MANUAL_TEST category. 44 * <pre> 45 * <intent-filter> 46 * <action android:name="android.intent.action.MAIN" /> 47 * <category android:name="android.cts.intent.category.MANUAL_TEST" /> 48 * </intent-filter> 49 * </pre> 50 * </li> 51 * <li>OPTIONAL: Add a meta data attribute to indicate what category of tests the activity 52 * should belong to. If you don't add this attribute, your test will show up in the 53 * "Other" tests category. 54 * <pre> 55 * <meta-data android:name="test_category" android:value="@string/test_category_security" /> 56 * </pre> 57 * </li> 58 * <li>OPTIONAL: Add a meta data attribute to indicate whether this test has a parent test. 59 * <pre> 60 * <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" /> 61 * </pre> 62 * </li> 63 * <li>OPTIONAL: Add a meta data attribute to indicate what features are required to run the 64 * test. If the device does not have all of the required features then it will not appear 65 * in the test list. Use a colon (:) to specify multiple required features. 66 * <pre> 67 * <meta-data android:name="test_required_features" android:value="android.hardware.sensor.accelerometer" /> 68 * </pre> 69 * </li> 70 * 71 * </ol> 72 */ 73 public class ManifestTestListAdapter extends TestListAdapter { 74 75 private static final String TEST_CATEGORY_META_DATA = "test_category"; 76 77 private static final String TEST_PARENT_META_DATA = "test_parent"; 78 79 private static final String TEST_REQUIRED_FEATURES_META_DATA = "test_required_features"; 80 81 private Context mContext; 82 83 private String mTestParent; 84 85 public ManifestTestListAdapter(Context context, String testParent) { 86 super(context); 87 mContext = context; 88 mTestParent = testParent; 89 } 90 91 @Override 92 protected List<TestListItem> getRows() { 93 94 /* 95 * 1. Get all the tests belonging to the test parent. 96 * 2. Get all the tests keyed by their category. 97 * 3. Flatten the tests and categories into one giant list for the list view. 98 */ 99 100 List<ResolveInfo> infos = getResolveInfosForParent(); 101 Map<String, List<TestListItem>> testsByCategory = getTestsByCategory(infos); 102 103 List<String> testCategories = new ArrayList<String>(testsByCategory.keySet()); 104 Collections.sort(testCategories); 105 106 List<TestListItem> allRows = new ArrayList<TestListItem>(); 107 for (String testCategory : testCategories) { 108 List<TestListItem> tests = filterTests(testsByCategory.get(testCategory)); 109 if (!tests.isEmpty()) { 110 allRows.add(TestListItem.newCategory(testCategory)); 111 Collections.sort(tests, new Comparator<TestListItem>() { 112 @Override 113 public int compare(TestListItem item, TestListItem otherItem) { 114 return item.title.compareTo(otherItem.title); 115 } 116 }); 117 allRows.addAll(tests); 118 } 119 } 120 return allRows; 121 } 122 123 List<ResolveInfo> getResolveInfosForParent() { 124 Intent mainIntent = new Intent(Intent.ACTION_MAIN); 125 mainIntent.addCategory(CATEGORY_MANUAL_TEST); 126 127 PackageManager packageManager = mContext.getPackageManager(); 128 List<ResolveInfo> list = packageManager.queryIntentActivities(mainIntent, 129 PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); 130 int size = list.size(); 131 132 List<ResolveInfo> matchingList = new ArrayList<ResolveInfo>(); 133 for (int i = 0; i < size; i++) { 134 ResolveInfo info = list.get(i); 135 String parent = getTestParent(info.activityInfo.metaData); 136 if ((mTestParent == null && parent == null) 137 || (mTestParent != null && mTestParent.equals(parent))) { 138 matchingList.add(info); 139 } 140 } 141 return matchingList; 142 } 143 144 Map<String, List<TestListItem>> getTestsByCategory(List<ResolveInfo> list) { 145 Map<String, List<TestListItem>> testsByCategory = 146 new HashMap<String, List<TestListItem>>(); 147 148 int size = list.size(); 149 for (int i = 0; i < size; i++) { 150 ResolveInfo info = list.get(i); 151 String title = getTitle(mContext, info.activityInfo); 152 String testName = info.activityInfo.name; 153 Intent intent = getActivityIntent(info.activityInfo); 154 String[] requiredFeatures = getRequiredFeatures(info.activityInfo.metaData); 155 TestListItem item = TestListItem.newTest(title, testName, intent, requiredFeatures); 156 157 String testCategory = getTestCategory(mContext, info.activityInfo.metaData); 158 addTestToCategory(testsByCategory, testCategory, item); 159 } 160 161 return testsByCategory; 162 } 163 164 static String getTestCategory(Context context, Bundle metaData) { 165 String testCategory = null; 166 if (metaData != null) { 167 testCategory = metaData.getString(TEST_CATEGORY_META_DATA); 168 } 169 if (testCategory != null) { 170 return testCategory; 171 } else { 172 return context.getString(R.string.test_category_other); 173 } 174 } 175 176 static String getTestParent(Bundle metaData) { 177 return metaData != null ? metaData.getString(TEST_PARENT_META_DATA) : null; 178 } 179 180 static String[] getRequiredFeatures(Bundle metaData) { 181 if (metaData == null) { 182 return null; 183 } else { 184 String value = metaData.getString(TEST_REQUIRED_FEATURES_META_DATA); 185 if (value == null) { 186 return null; 187 } else { 188 return value.split(":"); 189 } 190 } 191 } 192 193 static String getTitle(Context context, ActivityInfo activityInfo) { 194 if (activityInfo.labelRes != 0) { 195 return context.getString(activityInfo.labelRes); 196 } else { 197 return activityInfo.name; 198 } 199 } 200 201 static Intent getActivityIntent(ActivityInfo activityInfo) { 202 Intent intent = new Intent(); 203 intent.setClassName(activityInfo.packageName, activityInfo.name); 204 return intent; 205 } 206 207 static void addTestToCategory(Map<String, List<TestListItem>> testsByCategory, 208 String testCategory, TestListItem item) { 209 List<TestListItem> tests; 210 if (testsByCategory.containsKey(testCategory)) { 211 tests = testsByCategory.get(testCategory); 212 } else { 213 tests = new ArrayList<TestListItem>(); 214 } 215 testsByCategory.put(testCategory, tests); 216 tests.add(item); 217 } 218 219 List<TestListItem> filterTests(List<TestListItem> tests) { 220 List<TestListItem> filteredTests = new ArrayList<TestListItem>(tests); 221 PackageManager packageManager = mContext.getPackageManager(); 222 Iterator<TestListItem> iterator = filteredTests.iterator(); 223 while (iterator.hasNext()) { 224 TestListItem item = iterator.next(); 225 String[] requiredFeatures = item.requiredFeatures; 226 if (requiredFeatures != null) { 227 for (int i = 0; i < requiredFeatures.length; i++) { 228 if (!packageManager.hasSystemFeature(requiredFeatures[i])) { 229 iterator.remove(); 230 break; 231 } 232 } 233 } 234 } 235 return filteredTests; 236 } 237 } 238